/* Spentra Materials — homepage only: the scroll-led material story.
   Loaded after spentra.css, on index.html only.

   Base layout (phones, tablets, short windows, JavaScript off): stacked
   chapters, each on its own colour, each with its own still drawing.
   Desktop with JavaScript (1024px+ wide, 600px+ tall): one stage stays in view
   under the header while the chapters scroll past on the left; journey.js
   draws it. The header stays the only page-level bar: the chapter rail lives
   inside the stage. */

/* ---------- chapter colours: the light follows the story ---------- */
.chapter {
  --tone-bg: var(--bg); --tone-ink: var(--ink); --tone-muted: var(--ink-2); --tone-line: rgba(36, 33, 31, .14);
  background: var(--tone-bg); color: var(--tone-ink);
}
.chapter[data-tone="roast"]    { --tone-bg: #2C150C; --tone-ink: #F3E9DE; --tone-muted: #D2BFAE; --tone-line: rgba(243, 233, 222, .14); }
.chapter[data-tone="steel"]    { --tone-bg: #013B1F; --tone-ink: #ECE8E0; --tone-muted: #B8C9BE; --tone-line: rgba(236, 232, 224, .16); }
.chapter[data-tone="ember"]    { --tone-bg: #2C150C; --tone-ink: #F6E8DA; --tone-muted: #E2C7AE; --tone-line: rgba(246, 232, 218, .14); }
.chapter[data-tone="latte"]    { --tone-bg: #E5D5C0; --tone-ink: #24211F; --tone-muted: #4E453D; --tone-line: rgba(36, 33, 31, .16); }
.chapter[data-tone="charcoal"] { --tone-bg: #1D1B19; --tone-ink: #ECE8E0; --tone-muted: #BDB6AC; --tone-line: rgba(236, 232, 224, .14); }
.chapter[data-tone="roast"] :focus-visible, .chapter[data-tone="steel"] :focus-visible,
.chapter[data-tone="ember"] :focus-visible, .chapter[data-tone="charcoal"] :focus-visible { outline-color: var(--accent); }
.chapter[data-tone="steel"] + .chapter[data-tone="steel"] .chapter-inner { border-top: 1px solid var(--tone-line); }

/* ---------- base: stacked chapters ---------- */
.journey { padding: 0; }
.chapter { padding: clamp(48px, 9vw, 96px) 0; }
.journey .chapter { scroll-margin-top: var(--header-h); } /* a jump lands with the chapter's top edge right under the header */
.chapter-inner { display: grid; gap: 28px; align-items: center; }
.chapter-copy { max-width: 36rem; }
.chapter-copy > :last-child { margin-bottom: 0; }
.chapter .t-h2 { margin-bottom: 18px; color: var(--tone-ink); }
.chapter p { color: var(--tone-muted); font-size: 1.12rem; }
.chapter .btn-row { margin-top: 10px; }
.chapter .text-link { color: var(--tone-ink); }
.chapter-art { display: block; width: 100%; height: auto; max-height: min(52vw, 340px); }

/* hero */
.chapter-intro { padding-top: clamp(28px, 6vw, 72px); }
.chapter-intro .t-display { font-size: clamp(2rem, 1.1rem + 3vw, 2.8rem); line-height: 1.05; }
.chapter-intro .h1-brand {
  display: block; font-stretch: 112%; font-weight: 620; font-size: 0.42em;
  letter-spacing: 0; line-height: 1.3; color: var(--clay); margin-bottom: 0.4em;
}
.chapter-intro .t-lead { margin-top: 22px; color: var(--ink-2); }
.chapter-intro .btn-row { margin-top: 28px; }

/* COFFIRE: wordmark */
.chapter-coffire .coffire-wm { font-size: clamp(2.6rem, 1.6rem + 3vw, 3.9rem); color: var(--tone-ink); margin-bottom: 18px; }
.chapter-coffire .bean .bean-line { stroke: #2A1C15; }

/* ---------- partner page: who it's for and what to do, before the journey ---------- */
.partner-intro { background: var(--bg); padding: clamp(28px, 6vw, 64px) 0 clamp(20px, 4vw, 40px); }
.pi-inner { display: grid; gap: 28px; align-items: start; }
.partner-intro h1 { font-size: clamp(2.4rem, 1.2rem + 4.4vw, 4.2rem); line-height: 1.02; max-width: 14ch; text-wrap: balance; }
.partner-intro .t-lead { margin-top: 18px; color: var(--ink-2); max-width: 34rem; }
.partner-intro .btn-row { margin-top: 24px; }
.pi-side { display: grid; gap: 22px; padding: 22px 24px; border-radius: var(--r-md); background: color-mix(in srgb, var(--ink) 4%, transparent); border: 1px solid var(--line); }
.pi-label { font-size: 1rem; font-stretch: 110%; font-weight: 700; margin: 0 0 10px; }
.pi-who { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; }
.pi-who li { padding: 6px 12px; border-radius: 999px; border: 1px solid var(--line); background: var(--bg); font-size: .95rem; }
.pi-steps { margin: 0; padding: 0; list-style: none; counter-reset: pi; display: grid; gap: 10px; }
.pi-steps li { counter-increment: pi; display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 10px; align-items: baseline; color: var(--ink-2); }
.pi-steps li::before { content: counter(pi); display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--ink); color: var(--bg); font-size: .85rem; font-weight: 700; }
.pi-follow { justify-self: start; }
@media (min-width: 860px) {
  .pi-inner { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: clamp(32px, 5vw, 72px); align-items: center; }
}

/* the stage only exists in the desktop layout below */
.stage { display: none; }

/* tablets and small laptops: copy beside its drawing */
@media (min-width: 860px) {
  .chapter-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(32px, 5vw, 72px); }
  .chapter-art { max-height: 380px; }
}
@media (max-width: 859px) {
  .chapter-inner { grid-template-columns: minmax(0, 1fr); }
  .chapter-art { margin-inline: auto; max-width: 420px; }
}

/* =========================================================
   Desktop with JavaScript: the pinned story stage
   ========================================================= */
@media (min-width: 1024px) and (min-height: 600px) {
  html.js .story { display: grid; grid-template-columns: minmax(0, 1fr); }
  html.js .story > .chapter {
    grid-column: 1; position: relative; z-index: 1;
    display: flex; align-items: center; padding: 72px 0;
    min-height: 110vh; min-height: 110svh;
    background: transparent;
    pointer-events: none; /* the stage underneath gets the pointer; the copy takes it back */
  }
  html.js .story > .chapter:nth-of-type(1) { grid-row: 1; min-height: calc(100vh - var(--header-h)); min-height: calc(100svh - var(--header-h)); padding: 48px 0; }
  html.js .story > .chapter:nth-of-type(2) { grid-row: 2; }
  html.js .story > .chapter:nth-of-type(3) { grid-row: 3; }
  html.js .story > .chapter:nth-of-type(4) { grid-row: 4; }
  html.js .story > .chapter:nth-of-type(5) { grid-row: 5; }
  html.js .story > .chapter:nth-of-type(6) { grid-row: 6; }
  html.js .story > .chapter:nth-of-type(7) { grid-row: 7; }
  html.js .story .chapter-inner { display: block; width: 100%; border: 0; }
  html.js .story > .chapter[data-tone="steel"] + .chapter[data-tone="steel"] .chapter-inner { border-top: 0; }
  html.js .story .chapter-copy { width: max(38%, 22rem); max-width: none; pointer-events: auto; } /* about 38% of the composition */
  html.js .story .chapter-intro .chapter-copy { width: min(40vw, 35rem); } /* the hero keeps its size */
  html.js .story .chapter-intro .t-lead { font-size: clamp(1.12rem, 0.9rem + 0.35vw, 1.25rem); }
  html.js .story .chapter-art { /* the stage draws these; screen readers still get each description */
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%);
  }
  /* chapter text: editorial labels beside the stage, not headlines competing with it */
  html.js .story .t-h2 { font-size: clamp(1.55rem, 0.85rem + 1.15vw, 2.15rem); line-height: 1.12; max-width: 20ch; margin-bottom: 12px; }
  html.js .story .chapter:not(.chapter-intro) p { font-size: clamp(1rem, 0.92rem + 0.15vw, 1.08rem); line-height: 1.55; max-width: 34ch; }
  html.js .story .chapter .btn-row { margin-top: 6px; }
  html.js .story .chapter-coffire .coffire-wm { font-size: clamp(2rem, 1.1rem + 1.5vw, 2.7rem); margin-bottom: 12px; }
  html.js .story .chapter-intro .t-display { font-size: clamp(2.2rem, 3.2vw, 3.5rem); }

  html.js .story > .stage {
    display: block; grid-column: 1; grid-row: 1 / 8; align-self: start;
    position: sticky; top: var(--header-h); z-index: 0;
    height: calc(100vh - var(--header-h)); height: calc(100svh - var(--header-h));
    overflow: hidden; background: var(--bg); color: var(--ink);
  }
  .stage-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: pan-y; }

  /* chapter rail: where you are in the story, at the stage's right edge */
  .rail { position: absolute; right: clamp(14px, 1.8vw, 30px); top: 50%; transform: translateY(-50%); z-index: 2; }
  .rail ol { position: relative; display: grid; gap: 2px; }
  .rail ol::before, .rail ol::after { content: ""; position: absolute; right: 0; top: 16px; width: 1.5px; background: currentColor; }
  .rail ol::before { bottom: 16px; opacity: .2; }
  .rail ol::after { height: calc((100% - 32px) * var(--p, 0)); opacity: .75; }
  .rail a {
    display: flex; align-items: center; justify-content: flex-end; gap: 12px;
    min-height: 32px; padding-left: 8px; color: inherit; text-decoration: none;
    font-size: .82rem; font-weight: 560; white-space: nowrap;
  }
  .rail a::after { content: ""; flex: none; width: 9px; height: 1.5px; background: currentColor; opacity: .45; transition: width .25s var(--ease), opacity .25s var(--ease); }
  .rail-label { opacity: 0; transform: translateX(6px); transition: opacity .25s var(--ease), transform .25s var(--ease); }
  .rail a[aria-current]::after { width: 22px; opacity: 1; }
  .rail a[aria-current] .rail-label,
  .rail:hover .rail-label, .rail:focus-within .rail-label { opacity: 1; transform: none; }
  .rail:hover a:not([aria-current]) .rail-label, .rail:focus-within a:not([aria-current]) .rail-label { opacity: .7; }
  .rail a:hover .rail-label { opacity: 1; }
  .rail a:focus-visible { outline-color: currentColor; outline-offset: 2px; }
}

/* ---------- very narrow phones: the hero headline keeps inside 320px ---------- */
@media (max-width: 359px) {
  .chapter-intro .t-display { font-size: 1.85rem; }
  .partner-intro h1 { font-size: 2.1rem; }
}

/* =========================================================
   Phones, tablets and short windows (the stacked layout): one continuous
   material journey instead of a stack of separate blocks.
   - Each chapter's colour melts into the next over its bottom padding, so
     there is no hard seam (the copy always sits on a solid colour).
   - trail.js draws one dotted grounds thread from each drawing to the next,
     along the right-hand gutter, and (with motion) lets each drawing settle
     into its still picture as it comes into view. A chapter that leaves the
     screen mid-way snaps to its finished picture; nothing loops for long.
   ========================================================= */
@media not all and (min-width: 1024px) and (min-height: 600px) {
  .story { position: relative; overflow-x: clip; }
  .story > .chapter {
    --blend: 120px;
    padding-top: 44px; padding-bottom: calc(var(--blend) - 12px);
    background: linear-gradient(to bottom, var(--tone-bg) calc(100% - var(--blend)), var(--next-bg, var(--tone-bg)) 100%);
    background: linear-gradient(to bottom, var(--tone-bg) calc(100% - var(--blend)),
      color-mix(in srgb, var(--tone-bg) 55%, var(--next-bg, var(--tone-bg))) calc(100% - var(--blend) * .45), var(--next-bg, var(--tone-bg)) 100%);
  }
  .story > .chapter-intro { padding-top: clamp(24px, 5vw, 48px); }
  #ch-intro   { --next-bg: #2C150C; }
  #ch-problem { --next-bg: #013B1F; }
  #ch-pickup  { --next-bg: #013B1F; }
  #ch-how     { --next-bg: var(--footer-bg); --blend: 72px; }
  #ch-coffire { --next-bg: #E5D5C0; }
  #ch-story   { --next-bg: #1D1B19; }
  #ch-next    { --next-bg: var(--footer-bg); --blend: 72px; }
  #ch-next .chapter-inner { gap: 8px; }
  .chapter[data-tone="steel"] + .chapter[data-tone="steel"] .chapter-inner { border-top: 0; }
  .story .chapter-inner { gap: 20px; }
  .chapter-intro .t-lead { margin-top: 16px; }
  .chapter-intro .btn-row { margin-top: 22px; }
  .journey .chapter { scroll-margin-top: calc(var(--header-h) + 1px); }
  #ch-intro .chapter-art { max-height: min(96vw, 410px); }

  /* the grounds thread between drawings (built by trail.js; decorative) */
  .thread-layer { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
  .thread { position: absolute; left: 0; overflow: visible; }
  .thread .t-dots { fill: none; stroke: #B48A64; stroke-opacity: .75; stroke-width: 3.4; stroke-linecap: round; stroke-dasharray: 0 7 0 13 0 9 0 16 0 6 0 11; }
  .thread .t-dots2 { fill: none; stroke: #8A6249; stroke-opacity: .7; stroke-width: 2.4; stroke-linecap: round; stroke-dasharray: 0 15 0 8 0 19 0 12; stroke-dashoffset: 5; }
  .thread .t-reveal { fill: none; stroke: #fff; stroke-width: 12; stroke-linecap: round; }
  html.trail-on .thread .t-reveal { stroke-dasharray: 1; animation: tr-thread 1.4s cubic-bezier(.45, 0, .3, 1) both paused; }
  html.trail-on .thread.is-in .t-reveal { animation-play-state: running; }
  html.trail-on .thread.is-done .t-reveal { animation: none; stroke-dasharray: none; }
  @keyframes tr-thread { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
  html.trail-on .story .chapter-art { overflow: visible; }

  /* shared: parts settle from where they started */
  html.trail-on .chapter-art .gl,
  html.trail-on .chapter-art [class^="m-"] { transform-box: fill-box; transform-origin: 50% 50%; }
  @keyframes tr-fall { from { opacity: 0; transform: translate(var(--dx), var(--dy)); } to { opacity: 1; transform: none; } }
  @keyframes tr-gather { from { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(1.15); } to { opacity: 1; transform: none; } }
  @keyframes tr-in { from { opacity: 0; } to { opacity: 1; } }

  /* Start: one scroll-driven handoff (trail.js sets the time from the scroll position; reversible).
     On a 0-5s timeline: 0-40% both shots pour and the cups fill; 40-56% the portafilter comes out and
     tips; 52-94% the spent puck travels to the vessel and drops into its opening; 56-80% the portafilter
     goes back. 100% is the still drawing. */
  html.trail-on [data-chapter="intro"] .m-stream { animation: tr-h-stream 5s linear both paused; }
  @keyframes tr-h-stream { 0% { opacity: 0; } 2%, 37% { opacity: 1; } 41%, 100% { opacity: 0; } }
  html.trail-on [data-chapter="intro"] .m-fill { transform-origin: 50% 100%; animation: tr-h-fill 5s linear both paused; }
  @keyframes tr-h-fill { 0%, 3% { transform: scaleY(0); animation-timing-function: cubic-bezier(.4, 0, .4, 1); } 40%, 100% { transform: none; } }
  html.trail-on [data-chapter="intro"] .m-pf { transform-box: view-box; transform-origin: 400px 451px; animation: tr-h-pf 5s linear both paused; }
  @keyframes tr-h-pf {
    0%, 40% { transform: none; animation-timing-function: cubic-bezier(.4, 0, .3, 1); }
    52%, 62% { transform: translate(26px, 34px) rotate(18deg); animation-timing-function: cubic-bezier(.4, 0, .3, 1); }
    78%, 100% { transform: none; }
  }
  html.trail-on [data-chapter="intro"] .m-puck { animation: tr-h-puck 5s linear both paused; }
  @keyframes tr-h-puck {
    0%, 40% { transform: translate(-178px, -159px) scale(.9, .5); animation-timing-function: cubic-bezier(.4, 0, .3, 1); }
    52%     { transform: translate(-150px, -126px) rotate(18deg) scale(.9, .55); animation-timing-function: ease-in-out; }
    70%     { transform: translate(-84px, -168px) rotate(28deg) scale(.95, .75); animation-timing-function: ease-in-out; }
    86%     { transform: translate(0, -46px) rotate(10deg); animation-timing-function: cubic-bezier(.5, 0, .7, .4); }
    94%     { transform: translate(0, 3px); animation-timing-function: ease-out; }
    100%    { transform: none; }
  }

  /* The grounds: layers fall and settle into the pile */
  html.trail-on [data-chapter="problem"] .gl { animation: tr-fall 1.3s cubic-bezier(.25, .7, .3, 1) both; animation-delay: calc(.15s + var(--k) * 140ms); }

  /* Pickup: grounds drop into the bin, the lid settles, the QR code gets one glint */
  html.trail-on [data-chapter="pickup"] .gl { animation: tr-fall 1.2s cubic-bezier(.3, .6, .3, 1) both; animation-delay: calc(.1s + var(--k) * 130ms); }
  html.trail-on [data-chapter="pickup"] .m-lid { transform-origin: 0 100%; animation: tr-lid .8s cubic-bezier(.3, .7, .4, 1) 1s both; }
  @keyframes tr-lid { 0% { opacity: 0; transform: translateY(-34px) rotate(-6deg); } 65% { opacity: 1; transform: translateY(3px) rotate(.6deg); } 100% { transform: none; } }
  html.trail-on [data-chapter="pickup"] .m-glint { animation: tr-glint 1.2s ease-in-out 1.9s both; }
  @keyframes tr-glint { 0%, 100% { opacity: 0; } 45% { opacity: .9; } }

  /* How it works: one scan across the QR code, then the phone confirms */
  html.trail-on [data-chapter="how"] .m-scan { animation: tr-scan 1.2s cubic-bezier(.45, 0, .55, 1) .3s both; }
  @keyframes tr-scan { 0% { opacity: 0; transform: none; } 12%, 85% { opacity: 1; } 100% { opacity: 0; transform: translateY(106px); } }
  html.trail-on [data-chapter="how"] .m-link { animation: tr-in .5s ease 1.1s both; }
  html.trail-on [data-chapter="how"] .m-phone { animation: tr-phone .6s cubic-bezier(.3, .7, .3, 1) 1.3s both; }
  @keyframes tr-phone { from { opacity: 0; transform: translate(26px, 10px); } to { opacity: 1; transform: none; } }
  html.trail-on [data-chapter="how"] .m-check { stroke-dasharray: 1; animation: tr-check .45s ease-out 1.95s both; }
  @keyframes tr-check { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

  /* COFFIRE: grounds gather into the blocks and log, the flame lights and flickers briefly */
  html.trail-on [data-chapter="coffire"] .gl { animation: tr-gather 1.3s cubic-bezier(.25, .7, .3, 1) both; animation-delay: calc(.1s + var(--k) * 120ms); }
  html.trail-on [data-chapter="coffire"] .m-fglow { animation: tr-in 1.2s ease 1.6s both; }
  html.trail-on [data-chapter="coffire"] .m-flame { transform-origin: 50% 100%; animation: tr-ignite .9s cubic-bezier(.3, .7, .4, 1) 1.5s both, tr-flicker 1.6s ease-in-out 2.4s 3; }
  @keyframes tr-ignite { 0% { opacity: 0; transform: scale(.15); } 60% { opacity: 1; transform: scale(1.06); } 100% { transform: none; } }
  @keyframes tr-flicker { 0%, 100% { transform: none; } 50% { transform: scale(.98, 1.03); } }

  /* Our story: both pours, then a little steam and warmth over the two cups */
  html.trail-on [data-chapter="story"] .m-pour { transform-origin: 50% 0; animation: tr-pour 1.1s cubic-bezier(.4, 0, .4, 1) .3s both; }
  @keyframes tr-pour { from { transform: scaleY(0); } to { transform: none; } }
  html.trail-on [data-chapter="story"] .m-steam { animation: tr-steam 1.4s ease-out 1s both; }
  @keyframes tr-steam { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  html.trail-on [data-chapter="story"] .m-warm { animation: tr-warm 3.2s ease-in-out 1.3s 2 both; }
  html.trail-on [data-chapter="story"] .m-warm + .m-warm { animation-delay: 1.7s; }
  @keyframes tr-warm { 0% { opacity: 0; transform: translateY(4px); } 40% { opacity: 1; } 100% { opacity: 0; transform: translateY(-10px); } }

  /* What's next: one short pass. Loose grounds gather in from below and lift the COFFIRE bean into place,
     where it settles; the closed sample stays still. */
  html.trail-on [data-chapter="next"] .gl { animation: tr-fall 1.5s cubic-bezier(.25, .7, .3, 1) both; animation-delay: calc(var(--k) * 90ms); }
  html.trail-on [data-chapter="next"] .m-bean { transform-origin: 50% 70%; animation: tr-bean 1.9s cubic-bezier(.3, .6, .3, 1) .2s both; }
  @keyframes tr-bean {
    0%   { opacity: 0; transform: translateY(44px) rotate(-7deg); }
    35%  { opacity: 1; }
    70%  { transform: translateY(-4px) rotate(1deg); }
    100% { transform: none; }
  }

  /* nothing moves until its chapter is in view (trail.js finishes it if it leaves early) */
  html.trail-on .chapter:not(.is-in) .chapter-art * { animation-play-state: paused; }
}
@media (min-width: 1024px) and (min-height: 600px) { .thread-layer { display: none; } }
