/* Layout: typography is the demo. One chip language (ink box, -2°, Prompt 600) runs from the h1 slot to the
   footer sign-off. Hero = 3-column stage (poster | copy | poster) on the brand gradient; below: the projects
   header, two dark "chapter" bands in each product's own colours, the Raspberry Pi circuit band, end credits. */
:root{
  --spice-1:#FF4E50; --spice-2:#FC913A;
  --spice-grad:linear-gradient(135deg,var(--spice-1),var(--spice-2));
  --paper:#fffaf7; --paper-2:#ffffff; --paper-3:#fff1ec;
  --ink:#23303d; --ink-2:#4c5a68; --muted:#776763; /* 5.2:1 on --paper at 13px */
  --line:rgba(255,78,80,.16);
  --card-shadow:0 18px 40px -18px rgba(90,30,20,.35);
  /* fixed in both themes: the hero gradient and the dark bands never invert */
  --hero-ink:#23303d; --hero-ink-deep:#0b1219; --hero-ink-fg:#ffffff; /* deep: ≥4.5:1 for small text on the darkened hero centre */
  /* product worlds (--w<k>-bg/-fg/-line) are emitted by index.html from each content module's `colors` */
  --band:#23303d;
  --nav-light:rgba(255,250,247,.78);
  /* chip colours on themed paper follow --ink/--paper so the box stays visible in dark mode */
  --chip-bg:var(--hero-ink); --chip-fg:var(--hero-ink-fg);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ease-layout:cubic-bezier(.22,1,.36,1);
  --ease-morph:cubic-bezier(.33,0,.2,1);
  --ease-pop:cubic-bezier(.33,1,.68,1);
  --ease-flight:cubic-bezier(.2,.9,.2,1);
  --ease-float:cubic-bezier(.45,0,.55,1);
  --ease-wobble:cubic-bezier(.34,1.56,.64,1);
  --f-display:'Prompt',system-ui,'Noto Sans Thai',sans-serif;
  --f-body:'Sarabun','Prompt',system-ui,'Noto Sans Thai',sans-serif;
  /* three radii only: pill controls, cards (posters, bands), chips */
  --r-pill:999px; --r-card:20px; --r-chip:.3em;
  --grain:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  --exit:0;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    color-scheme:dark;
    --paper:#191315; --paper-2:#231a1c; --paper-3:#2b1f21;
    --ink:#f6ece8; --ink-2:#d7c8c3; --muted:#a8958f;
    --line:rgba(255,145,58,.22);
    --card-shadow:0 18px 40px -18px rgba(0,0,0,.6);
    --band:#1b2430; --nav-light:rgba(25,19,21,.78);
  }
}
/* reserved for a manual theme toggle (nothing sets data-theme yet); mirrors the media block above */
:root[data-theme="dark"]{
  color-scheme:dark;
  --paper:#191315; --paper-2:#231a1c; --paper-3:#2b1f21;
  --ink:#f6ece8; --ink-2:#d7c8c3; --muted:#a8958f;
  --line:rgba(255,145,58,.22);
  --card-shadow:0 18px 40px -18px rgba(0,0,0,.6);
  --band:#1b2430; --nav-light:rgba(25,19,21,.78);
}

*{box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:96px}
html:not(.motion){scroll-behavior:auto}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--f-body);font-size:16.5px;line-height:1.7;-webkit-font-smoothing:antialiased;overflow-x:hidden;overflow-x:clip}
/* overflow:hidden before overflow:clip everywhere: iOS 15 rejects clip and would fall back to visible */
.gutter{padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px))}
a{color:inherit}
h1,h2,h3{text-wrap:balance;margin:0}
p{margin:0}
ul,ol,dl,dd{margin:0;padding:0}
li{list-style:none}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}
a,button,[role="button"]{-webkit-tap-highlight-color:transparent} /* the designed :active press replaces the phone tap flash */
img{max-width:100%}
svg{display:inline-block;vertical-align:middle}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.skip{position:fixed;left:16px;top:-60px;z-index:60;background:var(--ink);color:var(--paper);padding:10px 16px;border-radius:var(--r-pill);font-family:var(--f-display);font-weight:500;text-decoration:none}
.skip:focus-visible{top:calc(96px + env(safe-area-inset-top,0px));outline:2px solid var(--spice-1);outline-offset:3px}
.below{content-visibility:auto;contain-intrinsic-block-size:auto 100svh}
.i1{--i:1}.i2{--i:2}.i3{--i:3}.i4{--i:4}.i5{--i:5}.i6{--i:6}.i7{--i:7}.i8{--i:8}.i9{--i:9}
.sprite{position:absolute;width:0;height:0;overflow:hidden}
.nw{white-space:nowrap} /* Thai phrase units the line breaker must not split */

/* ---------- chip language ---------- */
.chip{display:inline-grid;grid-template-columns:minmax(0,1fr);align-items:center;justify-items:center;padding:.16em .4em .14em;border-radius:var(--r-chip);rotate:-2deg;background:var(--chip-bg);color:var(--chip-fg);font:600 1em/1.3 var(--f-display);white-space:nowrap;vertical-align:middle;letter-spacing:0;text-transform:none}
.eyebrow{font-size:13px}
.chip.mini{font-size:13px}
.chip.num{font-size:12px;font-variant-numeric:tabular-nums;padding:.18em .5em .14em}

/* ---------- controls ---------- */
.btn{font-family:var(--f-display);font-weight:600;font-size:15.5px;padding:13px 24px;min-height:44px;border-radius:var(--r-pill);text-decoration:none;display:inline-flex;align-items:center;gap:8px;line-height:1.3;transition:background-color .2s var(--ease-out),color .2s var(--ease-out),scale 80ms var(--ease-out)}
.btn:active,.node:active,.pill:active,.ftool:active,.slot:active{scale:.97;transition-duration:80ms}
.btn-ink{background:var(--hero-ink);color:var(--hero-ink-fg);box-shadow:0 12px 26px -12px rgba(40,10,0,.6)}
/* deep ink outline on the gradient's darkened centre: ≥5.4:1 measured, the chip language already puts ink on orange */
.btn-ghost{border:1.5px solid var(--hero-ink-deep);color:var(--hero-ink-deep);background:rgba(255,255,255,.14)}
.btn-ghost:hover{background:rgba(255,255,255,.28)}
.btn-white{background:#fff;color:var(--band-ink,#23303d)}
.btn-white:hover{background:#fff5e8}
.arrow{transition:translate .25s var(--ease-out)}
@media (hover:hover){.btn:hover .arrow,.band:hover .btn-white .arrow{translate:2px -2px}}
/* focus rings per surface */
.hero :is(a,button,[tabindex]):focus-visible,.nav[data-on="dark"] :is(a,button):focus-visible{outline:3px solid #fff;outline-offset:3px}
.paper :is(a,button):focus-visible,.nav[data-on="light"] :is(a,button):focus-visible,.foot :is(a,button):focus-visible{outline:2px solid var(--spice-1);outline-offset:3px}
.band :is(a,button):focus-visible{outline:2px solid var(--wfg);outline-offset:4px}
.pi :is(a,button):focus-visible{outline:2px solid #fff;outline-offset:4px}

/* ---------- nav ---------- */
.nav{position:fixed;z-index:50;top:calc(16px + env(safe-area-inset-top,0px));left:0;right:0;margin-inline:auto;width:min(984px,100% - 32px);height:60px;transition:opacity .28s var(--ease-out),translate .32s var(--ease-out)}
.nav-in{height:100%;border-radius:var(--r-pill);display:flex;align-items:center;justify-content:space-between;gap:16px;padding:0 10px 0 12px;transition:background-color .7s var(--ease-morph),color .7s var(--ease-morph),box-shadow .7s var(--ease-morph)}
.nav[data-on="dark"] .nav-in{background:rgba(35,48,61,.42);-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);box-shadow:inset 0 0 0 1px rgba(255,255,255,.35);color:#fff}
.nav[data-on="light"] .nav-in{background:var(--nav-light);-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);box-shadow:inset 0 0 0 1px var(--line),0 10px 30px -18px rgba(90,30,20,.35);color:var(--ink)}
.nav.is-hidden{opacity:0;translate:0 calc(-100% - 24px);pointer-events:none}
html:not(.js) .nav{position:absolute} /* no script = no surface switching: scroll away with the hero instead of floating white on paper */
.brand{display:flex;align-items:center;gap:10px;text-decoration:none;min-height:44px;padding-right:8px}
.brand img{width:32px;height:32px;display:block}
.brand .bname{font-family:var(--f-display);font-weight:600;font-size:18px;line-height:1.3}
.brand .slogan{font-family:var(--f-display);font-size:13px;line-height:1.3;white-space:nowrap}
.nav-links{display:flex;align-items:center;gap:2px}
.nav-link{font-family:var(--f-display);font-weight:500;font-size:14px;line-height:1.3;padding:9px 12px;min-height:40px;display:inline-flex;align-items:center;text-decoration:none;border-radius:var(--r-pill);transition:background-color .2s var(--ease-out)}
.nav-link:hover{background:rgba(255,255,255,.12)}
.nav[data-on="light"] .nav-link:hover{background:var(--paper-3)}
.btn-nav{padding:9px 16px;font-size:14px;min-height:40px;transition:background-color .7s var(--ease-morph),color .7s var(--ease-morph),scale 80ms var(--ease-out)}
.nav[data-on="dark"] .btn-nav{background:#fff;color:var(--hero-ink)}
.nav[data-on="light"] .btn-nav{background:var(--ink);color:var(--paper)}
@media (max-width:640px){
  .nav{height:52px}
  .nav-in{padding:0 6px 0 10px}
  /* no backdrop blur on phones (the page's most expensive compositing effect); go nearly solid instead */
  .nav[data-on="dark"] .nav-in,.nav[data-on="light"] .nav-in{-webkit-backdrop-filter:none;backdrop-filter:none}
  .nav[data-on="light"] .nav-in{background:color-mix(in srgb,var(--paper) 94%,transparent)}
  .brand .slogan{display:none}
  .btn-nav{padding:7px 14px;min-height:36px}
  .nav-link{padding:7px 10px;min-height:36px}
}

/* ---------- hero ---------- */
/* Hero sits 56px short of the viewport so the paper edge shows under the fold: a visible edge beats a scroll cue.
   Background stack, top to bottom: grounding gradient (darker floor), centre scrim behind the copy (white h1 ≥3:1),
   the two brand radials, the fixed brand gradient. */
.hero{position:relative;min-height:min(100svh - 56px,900px);display:grid;grid-template-rows:auto 1fr;color:#fff;overflow:hidden;overflow:clip;
  background:
    linear-gradient(180deg,transparent 55%,rgba(120,20,30,.28)),
    radial-gradient(55% 60% at 50% 55%,rgba(120,20,10,.30),transparent 70%),
    radial-gradient(60% 50% at 20% 10%,rgba(255,140,110,.55),transparent 70%),
    radial-gradient(50% 60% at 85% 95%,rgba(200,40,50,.45),transparent 70%),
    var(--spice-grad)}
.hero .grain{position:absolute;inset:0;z-index:0;pointer-events:none;background:var(--grain) 0 0/240px 240px;opacity:.1;mix-blend-mode:soft-light}
.hero-top{height:calc(92px + env(safe-area-inset-top,0px))}
/* poster | copy | poster. Side columns are a share of the stage (26%) so the copy column never collapses at 1100px;
   posters tuck 6% under the copy column (copy is z-index 2) so the trio reads as one composition, not three islands. */
.stage{position:relative;z-index:1;width:100%;max-width:1440px;margin:0 auto;display:grid;grid-template-columns:minmax(220px,26%) minmax(0,1fr) minmax(220px,26%);grid-template-areas:"pl copy pr" "pl ctas pr";gap:0 12px;align-content:center;padding-inline:max(24px,env(safe-area-inset-left,0px)) max(24px,env(safe-area-inset-right,0px));padding-block:16px 48px}
.copy{grid-area:copy;align-self:end;text-align:center;container-type:inline-size;position:relative;z-index:2;opacity:calc(1 - var(--exit))}
.ctas{grid-area:ctas;align-self:start;display:flex;flex-wrap:wrap;justify-content:center;gap:12px;margin-top:28px;position:relative;z-index:2;opacity:calc(1 - var(--exit))}
/* the posters tuck under the copy column: the blank parts of these z-index:2 boxes must not steal the pointer */
.copy,.ctas{pointer-events:none}
.copy :is(.wc,.tilt,.sub),.ctas .btn{pointer-events:auto}
.pslot{align-self:center;width:min(100%,380px);position:relative;z-index:1}
.pslot-l{grid-area:pl;justify-self:end;translate:calc(6% + var(--exit) * -6vw) calc(var(--exit) * -40px);opacity:calc(1 - var(--exit) * .3)}
.pslot-r{grid-area:pr;justify-self:start;translate:calc(-6% + var(--exit) * 6vw) calc(var(--exit) * -40px);opacity:calc(1 - var(--exit) * .3)}

.hero h1{font-family:var(--f-display);font-weight:600;font-size:clamp(2.3rem,5.8vw,4.6rem);line-height:1.3;text-shadow:0 2px 24px rgba(120,20,10,.14)}
@supports (width:1cqi){.hero h1{font-size:clamp(2.3rem,9.6cqi,4.6rem)}}
.line{display:flex;justify-content:center;align-items:center;white-space:nowrap}
.l1{gap:.3em}
.wc{display:inline-block;overflow:hidden;overflow:clip;padding:.3em .12em;margin:-.3em -.12em}
.w{display:inline-block}
.tilt{display:inline-block;rotate:-2deg;transition:rotate .35s var(--ease-wobble),scale .35s var(--ease-wobble)}
@media (hover:hover){.tilt:hover{rotate:-4deg;scale:1.03}} /* phones keep :hover after a tap: the wobble would stick */
.slot{position:relative;rotate:none;cursor:pointer;transition:width 0s,scale 80ms var(--ease-out)}
.slot.is-rolling{overflow:hidden;overflow:clip}
.st{grid-area:1/1;display:inline-flex;align-items:center;white-space:nowrap;justify-self:center}
/* once widths are measured the box always equals its content, except during intro beat 1 when the chip is
   snapped to its no-ๆ width: anchor the text left so ของเผ็ด stays put while ๆ lands beyond the right edge */
.slot[data-chip="snap"] .st{justify-self:start}
.st:not(.is-on):not(.is-out){visibility:hidden}
.em{width:1em;height:1em;border-radius:50%;object-fit:cover;display:block;flex:0 0 auto;margin-right:.25em;box-shadow:0 0 0 .06em rgba(255,255,255,.55)}
.ym{display:inline-block;margin-left:.06em} /* optical gap before ไม้ยมก, same convention as the body's &nbsp;ๆ */
.hero .sub{font-family:var(--f-display);font-weight:500;font-size:clamp(1.05rem,1.7vw,1.3rem);line-height:1.5;margin:22px auto 0;max-width:34em;color:var(--hero-ink-deep);text-wrap:balance}
@media (min-width:1100px){.hero .sub{max-width:min(34em,100% - 48px)}} /* clear of the posters tucked 6% under the copy column */

/* posters */
.poster{display:block;aspect-ratio:3/4;width:100%;text-decoration:none;color:#fff;border-radius:var(--r-card);position:relative}
.pbox{--sel:0;position:absolute;inset:0;border-radius:var(--r-card);rotate:calc(var(--r) * (1 - var(--sel)));translate:0 calc(var(--ty) - var(--sel) * 10px);scale:calc(1 + var(--sel) * .03);transition:rotate .32s var(--ease-layout),translate .32s var(--ease-layout),scale .32s var(--ease-layout),opacity .32s var(--ease-layout)}
.pslot-l .pbox{--r:-5deg;--ty:6%}
.pslot-r .pbox{--r:4deg;--ty:-6%}
/* SELECT comes from the lap (.is-selected), keyboard focus or the pointer; the pointer/focus wins over a stale lap
   state (the hovered card is never dimmed, its sibling is forced down even while it carries .is-selected).
   One selector per rule: a list containing :has() is dropped whole by browsers without :has() support. */
.poster:is(:focus-visible,.is-selected) .pbox{--sel:1}
.poster.is-dim:not(:hover,:focus-visible) .pbox{opacity:.8}
.stage:has(.poster:focus-visible) .poster:not(:focus-visible) .pbox{--sel:0;opacity:.8}
@media (hover:hover){
  .poster:hover .pbox{--sel:1}
  .stage:has(.poster:hover) .poster:not(:hover) .pbox{--sel:0;opacity:.8}
}
/* pointer devices: hit-test a static pad on the <a>, never the moving card. Hovering lifts the card away from the
   cursor, the next 1px move found nothing under it, :hover dropped, the card tilted back: a raise/drop loop along
   every tilted edge. The pad covers the resting tilt, the lifted/scaled card and the ±6px idle float. */
@media (hover:hover) and (pointer:fine){
  .pbox{pointer-events:none}
  .poster::after{content:"";position:absolute;inset:-5% -7%}
  .pslot-l .poster::after{bottom:-12%} /* --ty:6% keeps the left card low even when lifted */
  .pslot-r .poster::after{top:-12%}    /* ...and the right card high */
}
@media (hover:hover) and (pointer:fine) and (max-width:1099px){.poster::after{inset:-3% -4%}} /* --ty:0 here; 4% stays inside the 12px gap */
.hero .poster:focus-visible{outline:none}
.poster:focus-visible .pfloat{outline:3px solid #fff;outline-offset:3px} /* the ring follows the card, not the static <a> box */
.pfloat{position:absolute;inset:0;border-radius:var(--r-card);overflow:hidden;overflow:clip;background:var(--hero-ink);box-shadow:0 24px 60px -20px rgba(90,20,10,.45),0 4px 12px rgba(90,30,20,.14)}
.pfloat::after{content:"";position:absolute;inset:0;border-radius:var(--r-card);box-shadow:inset 0 0 0 1px rgba(255,255,255,.35);pointer-events:none}
.pimg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.pp{position:absolute;left:0;right:0;bottom:0;min-height:28%;background:var(--hero-ink);color:#fff;padding:12px 14px;display:flex;flex-direction:column;justify-content:center;gap:3px}
.pn{display:flex;align-items:center;gap:8px}
.pn b{font-family:var(--f-display);font-weight:600;font-size:16px;line-height:1.3}
/* label roller: the clip box is padded .22em (margin pulls it back) so Thai tone marks are never shaved;
   the hidden line parks 1.52em away (1.3em line + .22em pad) so it stays fully outside the box */
.roll{display:grid;box-sizing:content-box;height:1.3em;padding-block:.22em;margin-block:-.22em;overflow:hidden;overflow:clip;font-family:var(--f-display);font-size:13px;line-height:1.3}
.roll > span{grid-area:1/1;line-height:1.3em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:translate .15s var(--ease-out)}
.roll .a{opacity:.72}
.roll .b{translate:0 1.52em;color:#fff}
.roll .short{display:none}
.poster:is(:focus-visible,.is-selected) .roll .a{translate:0 -1.52em}
.poster:is(:focus-visible,.is-selected) .roll .b{translate:0 0}
.stage:has(.poster:focus-visible) .poster:not(:focus-visible) .roll .a{translate:none}
.stage:has(.poster:focus-visible) .poster:not(:focus-visible) .roll .b{translate:0 1.52em}
@media (hover:hover){
  .poster:hover .roll .a{translate:0 -1.52em}
  .poster:hover .roll .b{translate:0 0}
  .stage:has(.poster:hover) .poster:not(:hover) .roll .a{translate:none}
  .stage:has(.poster:hover) .poster:not(:hover) .roll .b{translate:0 1.52em}
}
@supports not (translate:0){ /* engines without individual transform properties: hide what relies on them */
  .roll .b,.peek-win{display:none}
  .tip{transform:translateX(-50%)}
  .tip::before{transform:translateX(-50%) rotate(45deg)}
}

/* idle float (desktop, after intro) */
@keyframes float-a{from{translate:0 -6px;rotate:-.6deg}to{translate:0 6px;rotate:.6deg}}
@keyframes float-b{from{translate:0 5px;rotate:.5deg}to{translate:0 -6px;rotate:-.6deg}}
@media (min-width:1100px){
  html.motion body.is-done .pslot-l .pfloat{animation:float-a 8s var(--ease-float) -2s infinite alternate}
  html.motion body.is-done .pslot-r .pfloat{animation:float-b 9.5s var(--ease-float) -5s infinite alternate}
}

/* intro pre-states: only while motion is allowed and the intro has not taken over */
html.motion:not(.is-live) :is(.nav,.hero .w,.hero .slot,.hero .sub,.hero .ctas .btn,.hero .poster){opacity:0}
html.motion:not(.is-live) .hero .ym{opacity:0}

/* mobile hero: nav → h1 → sub → poster row → CTAs. Posters fan with a 12px gap (no overlap): opposite tilts make
   the bottom corners converge, and a negative margin would push one panel's text under the other card. */
@media (max-width:1099px){
  .hero{min-height:0}
  .copy,.ctas{opacity:1} /* --exit is desktop-only; never let a stale value hide the copy after a breakpoint crossing */
  .hero-top{height:calc(116px + env(safe-area-inset-top,0px))} /* ≥36px air between the nav and the chip at rest */
  .stage{grid-template-columns:1fr auto auto 1fr;grid-template-areas:"copy copy copy copy" ". pl pr ." "ctas ctas ctas ctas";gap:0;padding-block:8px 44px}
  .copy{align-self:auto}
  .pslot{width:min(42vw,200px);margin-top:36px;translate:none;opacity:1}
  .pslot-l{justify-self:auto;margin-right:12px}
  .pslot-r{justify-self:auto}
  .pslot-l .pbox{--r:-3.5deg;--ty:0%}
  .pslot-r .pbox{--r:3.5deg;--ty:0%}
  .ctas{margin-top:32px}
  .hero .sub{margin-top:16px}
  .pp{padding:10px 12px}
  .pn b{font-size:15px}
}
/* tablets: the headline steps down with the posters so two cards do not sit under a desktop-size h1 */
@media (min-width:641px) and (max-width:1099px){
  .hero h1{font-size:clamp(2.3rem,6.4vw,3.6rem)}
  .pslot{width:min(34vw,280px)}
}
@media (max-width:640px){
  .hero-top{height:calc(104px + env(safe-area-inset-top,0px))}
}
@media (max-width:1280px){
  .roll .long{display:none}.roll .short{display:inline} /* short kind lines keep the roller one line on narrow cards */
}
@media (max-width:420px){
  .hero.no-em .em{display:none}
}
@media (max-width:359px){
  .pp{padding:10px 10px} /* 360px phones: the short kind line needs the extra 4px or it ellipsises */
}

/* ---------- paper sections ---------- */
.paper{background:var(--paper);color:var(--ink);--chip-bg:var(--ink);--chip-fg:var(--paper)}
.inner{max-width:1180px;margin:0 auto}

/* projects header (first paper section after the hero edge) */
.projects{padding-block:72px 56px}
.ph{max-width:760px}
.ph h2{font-family:var(--f-display);font-weight:500;font-size:clamp(1.8rem,3.6vw,2.8rem);line-height:1.3;margin-top:14px;display:flex;flex-wrap:wrap;align-items:center;gap:0}
.ph .hw{display:inline-block}
.lead{color:var(--ink-2);max-width:60ch;margin-top:12px;font-size:16.5px;line-height:1.7}
html.motion .ph.is-enter .eyebrow{animation:stamp .45s var(--ease-pop) both}
@keyframes stamp{from{scale:1.3;rotate:-8deg}to{scale:1;rotate:-2deg}}
@media (max-width:640px){.projects{padding-block:56px 40px}}

/* generic reveal */
html.motion .rv:not(.is-enter) [data-reveal]{opacity:0;translate:0 18px;scale:.96}
html.motion .rv.is-enter [data-reveal]{transition:opacity .52s var(--ease-out),translate .52s var(--ease-out),scale .52s var(--ease-out);transition-delay:calc(var(--i,0) * 70ms)}

/* bands */
.bands{display:grid;gap:28px;margin-top:36px}
.band{position:relative;background:var(--wbg);color:#fff;border-radius:var(--r-card);overflow:hidden;overflow:clip;display:grid;grid-template-columns:1fr 1.1fr;align-items:center;max-width:1180px;box-shadow:var(--card-shadow)}
.band.flip{grid-template-columns:1.1fr 1fr} /* even bands mirror: art left, text right */
.band::before{content:"";position:absolute;inset:0 0 auto;height:3px;background:var(--spice-grad);z-index:3}
.band::after{content:"";position:absolute;inset:0;background:var(--grain) 0 0/240px 240px;opacity:.04;mix-blend-mode:soft-light;pointer-events:none;z-index:2}
.wm{position:absolute;bottom:-.12em;font:400 18vw/1 var(--f-display);color:rgba(255,255,255,.07);pointer-events:none;-webkit-user-select:none;user-select:none;z-index:0;font-variant-numeric:tabular-nums}
.band:not(.flip) .wm{left:-.04em}
.band.flip .wm{right:-.04em}
.btext{position:relative;z-index:1;padding:clamp(28px,4vw,52px) clamp(24px,3.4vw,44px)}
.band.flip .btext{order:2}
.beyebrow{font-family:var(--f-display);font-weight:500;font-size:13px;line-height:1.4;color:var(--wfg);opacity:.8}
.tline{display:block;overflow:hidden;overflow:clip;padding-block:.22em;margin-block:-.22em}
.btitle{font-family:var(--f-display);font-weight:600;font-size:clamp(2.6rem,6vw,5rem);line-height:1.3;color:var(--wfg);margin-top:6px}
.tt{position:relative;display:inline-block}
.btag{font-family:var(--f-display);font-weight:500;font-size:clamp(1.1rem,1.8vw,1.4rem);line-height:1.4;color:var(--spice-2);margin-top:10px}
.bbody{color:rgba(255,255,255,.86);font-size:17.5px;margin-top:14px;max-width:48ch}
.bchips{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}
.bchips li{font-family:var(--f-display);font-weight:500;font-size:13px;line-height:1.4;padding:5px 11px;border-radius:var(--r-pill);border:1px solid var(--wline);color:#fff}
.bnote{display:flex;gap:8px;align-items:flex-start;margin-top:14px;font-size:14px;line-height:1.6;color:rgba(255,255,255,.72)}
.bnote svg{flex:0 0 auto;margin-top:5px}
.bgo{display:flex;align-items:center;gap:14px 18px;flex-wrap:wrap;margin-top:24px}
.url{font-family:var(--f-display);font-size:13.5px;color:rgba(255,255,255,.6)}
.quiet{font-family:var(--f-display);font-size:13.5px;color:var(--wfg);text-decoration:underline;text-underline-offset:4px;text-decoration-color:var(--wline);min-height:44px;display:inline-flex;align-items:center}
.quiet:hover{text-decoration-color:var(--wfg)}
/* "สื่อที่พูดถึง": press mentions, names only (no counts, they drift); url-less entries are plain text */
.press{display:flex;flex-wrap:wrap;align-items:center;gap:0 14px;margin-top:10px;font-family:var(--f-display);font-size:13px;line-height:1.4;color:rgba(255,255,255,.72)}
.press-k{color:var(--wfg);opacity:.8}
.press a,.press span:not(.press-k){min-height:44px;display:inline-flex;align-items:center;color:#fff}
.press a{text-decoration:underline;text-underline-offset:4px;text-decoration-color:var(--wline)}
.press a:hover{text-decoration-color:var(--wfg)}
.art{position:relative;display:block;align-self:stretch;min-height:560px;overflow:hidden;overflow:clip;z-index:1;box-shadow:inset 0 0 0 1px var(--wline);transition:box-shadow .3s var(--ease-out);container-type:size}
.cover{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;transition:scale .6s var(--ease-out)}
@media (hover:hover){
  .band:hover .art{box-shadow:inset 0 0 0 1px var(--wfg)}
  .band:hover .cover{scale:1.04}
}
/* hover-to-preview: the cover crossfades to an in-game shot that slowly settles from 1.08 to 1 (the <picture> only
   gives the img a source on fine-pointer devices, so phones never download it) */
.preview{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;opacity:0;scale:1.08;pointer-events:none;transition:opacity .35s var(--ease-out),scale .8s var(--ease-out)}
@media (hover:hover) and (pointer:fine){
  html.motion .art.has-preview:hover .preview{opacity:1;scale:1;transition:opacity .35s var(--ease-out),scale 6s linear}
}
/* one in-world gesture per band: the gold curtain wipes the art (.8s); the text block fades in as a single unit */
/* legacy transform on purpose: an engine without the `scale` property would otherwise paint the curtain permanently */
.curtain{position:absolute;inset:0;background:var(--wfg);transform:scaleX(0);transform-origin:left;z-index:2;pointer-events:none}
html.motion .band.is-enter .curtain{animation:curtain .8s both}
@keyframes curtain{0%{transform:scaleX(0);transform-origin:left;animation-timing-function:cubic-bezier(.76,0,.24,1)}38%{transform:scaleX(1);transform-origin:left}40%{transform:scaleX(1);transform-origin:right;animation-timing-function:cubic-bezier(.16,1,.3,1)}100%{transform:scaleX(0);transform-origin:right}}
html.motion .band.is-enter .cover{animation:settle 1.1s var(--ease-out)} /* no fill mode: a held `to` keyframe would outrank the hover zoom forever */
@keyframes settle{from{scale:1.06}to{scale:1}}
html.motion .band:not(.is-enter) .btext[data-reveal]{translate:none;scale:1}
.band.flash .art{box-shadow:inset 0 0 0 2px var(--wfg)}
/* peek: eyes slide out from behind the title's right edge */
.peek-win{position:absolute;left:100%;top:.42em;width:56px;height:22px;overflow:hidden;overflow:clip;pointer-events:none}
.peek{position:absolute;left:0;top:0;width:38px;height:22px;border-radius:11px;background:#0b140d;box-shadow:inset 0 0 0 1px rgba(233,201,116,.3);display:flex;align-items:center;justify-content:center;gap:8px;translate:-110% 0}
.peek i{width:6px;height:6px;border-radius:50%;background:#ffe9a8;display:block}
html.motion .band.is-enter .peek{animation:peek 9s linear 2s infinite}
html.motion .band.is-enter .peek i{animation:blink 9s linear 2s infinite}
@keyframes peek{0%,30%{translate:-110% 0}34.4%,47%{translate:14px 0}51.5%,100%{translate:-110% 0}}
@keyframes blink{0%,39%{scale:1 1}40.3%{scale:1 .1}41.6%,100%{scale:1 1}}
@media (max-width:859px){
  .band,.band.flip{grid-template-columns:1fr}
  .band.flip .btext{order:0}
  .art,.band.flip .art{order:-1;aspect-ratio:1/1;min-height:0}
  .band:hover .cover{scale:1}
  .press a,.press span:not(.press-k){min-height:40px}
  html.motion .band.is-enter .cover{animation:none}
  .bbody{max-width:none}
  .peek-win{display:none}
  .wm{font-size:28vw}
}
/* small tablets / landscape phones: a wide, capped art block instead of a full-screen portrait */
@media (min-width:600px) and (max-width:859px){
  .art,.band.flip .art{aspect-ratio:16/10;max-height:60svh}
}

/* ---------- pi band ---------- */
.pi{position:relative;background:var(--band);color:#fff;padding-block:96px 80px;overflow:hidden;overflow:clip;--chip-bg:#fff;--chip-fg:var(--band)}
.pi::before{content:"";position:absolute;inset:0 0 auto;height:3px;background:var(--spice-grad)}
.pi-inner{max-width:1180px;margin:0 auto;text-align:center}
.pi h2{font-family:var(--f-display);font-weight:500;font-size:clamp(1.6rem,3.2vw,2.6rem);line-height:1.3;color:#fffaf7;margin-top:14px}
.pi-sub{color:rgba(255,255,255,.75);max-width:58ch;margin:12px auto 0}
.circuit{position:relative;margin:56px auto 0;max-width:980px}
.wire{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}
/* the wire runs node centre to node centre (4 grid columns → 1/8 … 7/8), so the dash arrives under คุณ */
.wire-h{left:12.5%;width:75%}
.wire-v{display:none;top:22px;height:calc(100% - 44px)}
/* The wires are <line>s with % coordinates in an SVG without a viewBox: user units are pixels, so pathLength="1"
   normalises the real length and ".08 1" is one 8% dash. (A viewBox + non-scaling-stroke made Chromium lay the
   dashes out at viewBox scale, and an objectBoundingBox gradient on a straight line never paints at all.) */
.wire line{fill:none;stroke-width:1.5}
.wire .base{stroke:rgba(255,255,255,.28)}
.wire .dash{stroke:url(#spice-h);stroke-width:3;stroke-linecap:round;stroke-dasharray:.08 1;stroke-dashoffset:.52}
.wire-v .dash{stroke:url(#spice-v)}
.wire .draw{stroke:rgba(255,255,255,.28);stroke-dasharray:1 1;stroke-dashoffset:0}
/* under motion the .draw line is the wire: hidden until the band enters, then drawn in .9s (base stays for rest) */
html.motion .wire .base{display:none}
html.motion .pi:not(.is-enter) .draw{stroke-dashoffset:1}
.nodes{position:relative;display:grid;grid-template-columns:repeat(4,1fr);gap:16px;align-items:start}
.nodes li{position:relative;display:flex;justify-content:center;justify-self:center;width:max-content}
.node{font-family:var(--f-display);font-weight:500;font-size:13px;line-height:1.4;padding:11px 16px;min-height:44px;border-radius:10px;background:var(--band);box-shadow:inset 0 0 0 1px rgba(255,255,255,.3);color:#fff;transition:box-shadow .2s var(--ease-out),scale 80ms var(--ease-out);white-space:nowrap;position:relative}
.node.is-open{box-shadow:inset 0 0 0 1px rgba(255,255,255,.7)}
@media (hover:hover){.node:hover{box-shadow:inset 0 0 0 1px rgba(255,255,255,.7)}}
/* คุณ: 13px white on the gradient was 2.4:1; deep ink reads 6:1 (the chip language's ink-on-orange) */
.nodes li:last-child .node{background:var(--spice-grad);box-shadow:none;color:var(--hero-ink-deep);font-weight:600}
.tip{position:absolute;top:calc(100% + 10px);left:50%;translate:-50% 0;width:max-content;max-width:min(240px,80vw);background:#fffaf7;color:#23303d;font-family:var(--f-body);font-size:13.5px;line-height:1.5;padding:8px 12px;border-radius:10px;box-shadow:0 10px 30px -10px rgba(0,0,0,.5);opacity:0;visibility:hidden;transition:opacity .2s var(--ease-out),visibility 0s .2s;z-index:3;text-wrap:balance}
.tip::before{content:"";position:absolute;left:50%;top:-5px;width:10px;height:10px;background:#fffaf7;translate:-50% 0;rotate:45deg}
/* shown by pointer hover, keyboard focus or a tap (.is-open); one selector per rule (see the poster note).
   :focus-visible rather than :focus-within so an Android tap, which focuses the button, cannot pin the tip open */
.node.is-open + .tip{opacity:1;visibility:visible;transition:opacity .2s var(--ease-out),visibility 0s}
.nodes li:where(:has(.node:focus-visible)) .tip{opacity:1;visibility:visible;transition:opacity .2s var(--ease-out),visibility 0s}
@media (hover:hover){.nodes li:where(:hover) .tip{opacity:1;visibility:visible;transition:opacity .2s var(--ease-out),visibility 0s}}
.nodes li.is-muted .tip{opacity:0;visibility:hidden;transition:opacity .2s var(--ease-out),visibility 0s .2s} /* Escape; :where() above keeps this on top */
html.motion .pi.is-enter .draw{animation:draw .9s var(--ease-out) both}
@keyframes draw{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
html.motion .pi.is-enter .dash{animation:travel 2.4s linear .6s infinite}
@keyframes travel{from{stroke-dashoffset:1.08}to{stroke-dashoffset:0}}
html.motion .pi.is-enter .nodes li:last-child .node{animation:youpop 2.4s var(--ease-pop) 2.4s infinite}
@keyframes youpop{0%,78%{scale:1}86%{scale:1.08}100%{scale:1}}
html:not(.motion) .wire .dash{stroke-dasharray:.015 1;stroke-dashoffset:-.9} /* one static dot, just before คุณ */
@media (max-width:720px){
  .circuit{margin-top:40px;max-width:280px}
  .nodes{grid-template-columns:1fr;gap:56px;justify-items:center} /* room for a one-line tip under each node */
  .tip{pointer-events:none} /* stacked layout: an open tip must not swallow the tap meant for the node below it */
  .wire-h{display:none}.wire-v{display:block}
}
@media (max-width:640px){
  .pi{padding-block:64px 56px}
}

/* ---------- credits ---------- */
.credits{padding-block:72px 56px}
.cr-inner{max-width:760px;margin:0 auto;border-top:1px solid var(--line);padding-top:56px}
.cr-logo{width:72px;height:72px;display:block;border-radius:50%;background:var(--paper-3);padding:8px;margin-bottom:22px}
.credits h2{font-family:var(--f-display);font-weight:500;font-size:clamp(1.8rem,3.4vw,2.6rem);line-height:1.3;margin-top:14px}
.rows{margin-top:36px}
.row{display:grid;grid-template-columns:200px 1fr;gap:8px 28px;padding-block:18px;position:relative}
.row::before{content:"";position:absolute;inset:0 0 auto;height:1px;background:var(--line);transform-origin:left}
.row dt{font-family:var(--f-display);font-weight:500;font-size:13px;line-height:1.5;color:var(--muted);text-align:right;padding-top:5px}
.row dd{font-size:18px;line-height:1.5;color:var(--ink)}
.pills{display:flex;flex-wrap:wrap;gap:10px}
.pill{font-family:var(--f-display);font-size:13.5px;font-weight:500;color:var(--ink-2);text-decoration:none;padding:9px 16px;min-height:40px;display:inline-flex;align-items:center;border-radius:var(--r-pill);border:1px solid var(--line);background:var(--paper-2);transition:border-color .32s var(--ease-out),color .32s var(--ease-out),scale 80ms var(--ease-out)}
.pill:hover{border-color:var(--spice-1)} /* text stays --ink-2 (6.8:1); orange text on paper would be 3.3:1 */
html.motion .credits.is-enter .row[data-reveal]{transition-delay:calc(var(--i,0) * 90ms + 200ms)}
html.motion .credits:not(.is-enter) .row::before{scale:0 1}
html.motion .credits.is-enter .row::before{transition:scale .6s cubic-bezier(.22,1,.36,1);transition-delay:calc(var(--i,0) * 90ms + 300ms)}
@media (max-width:640px){
  .credits{padding-block:56px 40px}
  .row{grid-template-columns:1fr;gap:4px}
  .row dt{text-align:left;padding-top:0}
  .pills{display:grid;grid-template-columns:1fr 1fr}
  .pill{justify-content:center}
}

/* ---------- footer ---------- */
.foot{border-top:1px solid var(--line);padding-block:26px 34px;color:var(--muted);font-size:13.5px;line-height:1.7}
.foot-inner{max-width:1180px;margin:0 auto;display:flex;flex-wrap:wrap;gap:12px 28px;justify-content:space-between;align-items:center}
.foot-inner > span{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}
.ftool{font-family:var(--f-display);font-weight:500;font-size:13.5px;color:var(--ink-2);min-height:44px;display:inline-flex;align-items:center;gap:6px;padding:0 4px;text-decoration:underline;text-underline-offset:4px;text-decoration-color:var(--line);transition:color .2s var(--ease-out),scale 80ms var(--ease-out)}
.ftool:hover{color:var(--spice-1);text-decoration-color:currentColor}
.ftool:disabled{opacity:.5;cursor:default;text-decoration:none}
.ftools{gap:8px 18px}

/* ---------- reduced motion / resting state ---------- */
html:not(.motion) *,html:not(.motion) *::before,html:not(.motion) *::after{animation:none!important}
html:not(.motion) .pbox,html:not(.motion) .roll > span,html:not(.motion) .tilt{transition:none}
html:not(.motion) .poster:is(:hover,:focus-visible,.is-selected) .pbox{--sel:0}
html:not(.motion) .band:hover .cover{scale:1}
html:not(.motion) .nav{transition:none}
html:not(.motion) .nav.is-hidden{opacity:1;translate:0 0;pointer-events:auto}
@media (prefers-reduced-motion: reduce){html{scroll-behavior:auto}}
:root[data-motion="reduce"]{scroll-behavior:auto}
[data-scene="paused"],[data-scene="paused"] *{animation-play-state:paused!important;will-change:auto!important}

/* ---------- print ---------- */
@media print{
  .nav,.skip,.ftools,.grain,.curtain,.wm,.peek-win,.preview,.tip{display:none}
  /* the brand surfaces carry white text: print their backgrounds even with "background graphics" off */
  .hero,.band,.pi,.chip,.btn-ink,.pp{print-color-adjust:exact;-webkit-print-color-adjust:exact}
  .hero{--exit:0!important;min-height:0}
  [data-reveal],.hero :is(.w,.slot,.ym,.sub,.btn,.poster){opacity:1!important;translate:none!important;scale:none!important;transform:none!important}
  .band,.pi,.credits .row{break-inside:avoid}
  .below{content-visibility:visible}
}

/* ---------- 404 ---------- */
.nf{min-height:100svh;display:grid;place-items:center;text-align:center;padding-block:64px}
.nf-in{max-width:34em}
.nf .cr-logo{margin:0 auto 22px}
.nf h1{font-family:var(--f-display);font-weight:600;font-size:clamp(1.8rem,4vw,2.8rem);line-height:1.3;margin-top:14px}
.nf .lead{margin:12px auto 0}
.nf .btn{margin-top:28px}
.nf .btn-ink{background:var(--ink);color:var(--paper)} /* the fixed hero ink would vanish on dark paper */
