/* =============================================================================
   Milkbench — three cards floating over a full-window MuJoCo hand.

   Same shape as the dieHaut and Residency decks: the deck layer is pointer
   transparent except for the cards themselves, so a drag on the visible hand
   always reaches the WebGL canvas and a drag on a card always turns the page.

   The palette is the subject's own: a milk-white page, cocoa brown type, and
   teat pink as the one signal colour. The hand is lit for that ground.
   ========================================================================== */

:root{
  --bg:#f7f0e8;               /* milk */
  --bg-deep:#efe4d8;
  --ink:#3a2519;              /* cocoa, the cow's own brown */
  --muted:#8a6a58;
  --glass:rgba(255,251,247,.74);
  --glass-hi:rgba(255,255,255,.92);
  --line:rgba(58,37,25,.12);
  --line-hi:rgba(58,37,25,.24);
  --accent:#e8799a;           /* teat pink, the one signal colour */
  --accent-deep:#d4627f;
  --warm:#8a5a3c;             /* leather brown for numbers and the readout */
  --pending:#b8743a;
  --pad:16px;
  --safe-b:env(safe-area-inset-bottom,0px);
  --card-h:60svh;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}

*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font:400 15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased; overflow:hidden;
  overscroll-behavior:none; width:100%;
}
a{color:var(--accent)}
b{font-weight:650}

/* ---------- stage ---------- */
#stage{position:fixed; inset:0; z-index:0}
#viewport{position:absolute; inset:0}
#viewport canvas{display:block}
/* Three layers: a soft brown spot behind where the hand sits, so white PLA has
   something to stand off from on a cream page; a vignette at the edges; and
   the fade that carries the copy at the bottom. The spot moves with the
   composition, portrait here and landscape in the desktop block below. */
#scrim{
  position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(58% 28% at 50% 27%, rgba(58,37,25,.11), rgba(58,37,25,0) 72%),
    radial-gradient(120% 80% at 50% 6%, transparent 38%, rgba(239,228,216,.55) 100%),
    linear-gradient(to top, rgba(247,240,232,.97) 0%, rgba(247,240,232,.9) 34%,
                    rgba(247,240,232,.42) 58%, transparent 76%);
}

#swipezone{position:fixed; left:0; right:0; bottom:0; top:62%; z-index:10; display:none}
@media (max-width:899px){ #swipezone{display:block} }

/* ---------- boot ---------- */
#boot{
  position:fixed; inset:0; z-index:60; display:grid; place-items:center;
  background:var(--bg-deep); transition:opacity .5s ease;
}
#boot.is-done{opacity:0; pointer-events:none}
.boot__inner{text-align:center; padding:0 24px; max-width:420px}
.boot__msg{margin:0 0 14px; font-size:14px; letter-spacing:.04em; color:var(--ink)}
.boot__track{height:2px; background:var(--line); border-radius:2px; overflow:hidden}
.boot__track i{display:block; height:100%; width:0; background:var(--accent); transition:width .3s ease}
/* Until the MuJoCo module has landed there is no byte count to report, because
   the import that fetches it blocks the script that would do the counting. A
   sweep says "working" without claiming progress it cannot measure. */
#boot.is-waiting .boot__track i{width:34%; animation:sweep 1.3s ease-in-out infinite}
@keyframes sweep{
  0%{transform:translateX(-110%)} 100%{transform:translateX(330%)}
}
.boot__hint{margin:14px 0 0; font:400 11.5px/1.5 var(--mono); letter-spacing:.04em; color:var(--muted)}
#boot.is-failed .boot__track{display:none}
#boot.is-failed .boot__msg{color:var(--pending)}

/* ---------- top bar ---------- */
.topbar{
  position:fixed; z-index:30; top:0; left:0; right:0;
  display:flex; align-items:center; gap:10px; justify-content:space-between;
  padding:calc(env(safe-area-inset-top,0px) + 10px) var(--pad) 10px;
  background:linear-gradient(to bottom, rgba(247,240,232,.86), rgba(247,240,232,0));
}
.brand{
  display:flex; flex-direction:column; align-items:flex-start; gap:1px;
  background:none; border:0; padding:0; color:var(--ink); text-align:left; cursor:pointer;
}
.brand__mark{font-weight:700; font-size:18px; letter-spacing:-.015em}
.brand__accent{color:var(--accent)}
.brand__sub{font-size:11px; color:var(--muted)}
.topbar__actions{display:flex; gap:8px; flex-shrink:0}
.pill{
  display:inline-block; text-decoration:none; line-height:1.25;
  border:1px solid var(--line-hi); background:var(--glass); color:var(--ink);
  padding:7px 12px; border-radius:999px; font-size:12.5px; font-weight:500;
  cursor:pointer; backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
}
.pill:hover{background:var(--glass-hi)}
.pill--primary{background:var(--accent); border-color:var(--accent); color:#3a2519; font-weight:650}
.pill--primary:hover{background:var(--accent-deep)}
@media (max-width:520px){ .pill:not(.pill--primary){display:none} }

/* ---------- deck ---------- */
.deck{position:fixed; inset:0; z-index:20; pointer-events:none; overflow:hidden}
.track{display:flex; height:100%; width:100%; transition:transform .52s cubic-bezier(.4,.02,.2,1)}
/* min-width:0 on both the slide and the card is what keeps the deck the width
   of the window. A flex item defaults to min-width:auto, so the board table's
   own 520px minimum would otherwise push this slide wider than the viewport,
   and since every slide sits on one flex track it drags the other two with it:
   the hero ends up scrolled half off screen on a phone. */
.slide{
  flex:0 0 100%; min-width:0; height:100%; position:relative;
  display:flex; align-items:flex-end; justify-content:center;
  padding:0 var(--pad);
}

.card{
  pointer-events:auto;
  width:100%; min-width:0; max-width:820px;
  max-height:var(--card-h);
  margin:0 0 calc(var(--safe-b) + 84px);
  overflow-y:auto; overscroll-behavior:contain;
  text-shadow:0 1px 14px rgba(247,240,232,.95), 0 0 34px rgba(247,240,232,.75);
}
.card::-webkit-scrollbar{width:6px}
.card::-webkit-scrollbar-thumb{background:var(--line-hi); border-radius:3px}

.kicker{
  margin:0 0 12px; font:650 11.5px/1 var(--mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--accent);
}
.card h2{
  margin:0 0 16px; font-size:clamp(26px,5.6vw,44px); line-height:1.04;
  letter-spacing:-.028em; font-weight:800;
  text-shadow:0 2px 18px rgba(247,240,232,.95), 0 10px 50px rgba(247,240,232,.8);
}
.card p{margin:0 0 12px; max-width:56em; color:var(--ink)}

/* ---------- hero ---------- */
.slide--hero{align-items:flex-end}
.hero{
  pointer-events:auto; text-align:center; padding:0 8px; max-width:640px;
  margin-bottom:calc(var(--safe-b) + 96px);
  text-shadow:0 1px 14px rgba(247,240,232,.95), 0 0 34px rgba(247,240,232,.75);
}
.eyebrow{margin:0 0 10px; font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted)}
.hero h1{
  margin:0; font-size:clamp(48px,12vw,112px); line-height:.92; letter-spacing:-.045em; font-weight:800;
  text-shadow:0 8px 40px rgba(247,240,232,.8);
}
.hero__lede{margin:14px 0 8px; font-size:clamp(17px,4.2vw,24px); line-height:1.3}
.hero__lede b{color:var(--accent)}
.hero__sub{margin:0 auto 18px; max-width:440px; font-size:13.5px; color:var(--muted)}
.hero__hint{margin:12px 0 0; font-size:11.5px; color:var(--muted)}
.hero__hint #cycles{font-family:var(--mono); color:var(--ink)}

/* ---------- the two swept parameters ---------- */
.rig{
  display:grid; gap:9px; margin:0 auto 14px; max-width:400px;
  padding:13px 15px 12px; border-radius:12px;
  border:1px solid var(--line); background:var(--glass);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  text-shadow:none;
}
.rig__row{display:grid; grid-template-columns:104px 1fr 62px; gap:10px; align-items:center}
.rig__row label{font:500 10.5px/1 var(--mono); letter-spacing:.08em; text-transform:uppercase; color:var(--muted); text-align:left}
.rig__row output{font:500 12px/1 var(--mono); color:var(--ink); text-align:right; font-variant-numeric:tabular-nums}
.rig__note{margin:2px 0 0 !important; font-size:11px; color:var(--muted); text-align:left}
.rig__note b{color:var(--warm); font-family:var(--mono); font-weight:500}

input[type=range]{
  -webkit-appearance:none; appearance:none; width:100%; height:20px;
  background:transparent; cursor:pointer; margin:0;
}
input[type=range]::-webkit-slider-runnable-track{height:3px; border-radius:2px; background:var(--line-hi)}
input[type=range]::-moz-range-track{height:3px; border-radius:2px; background:var(--line-hi)}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none; appearance:none; width:14px; height:14px; border-radius:50%;
  background:var(--accent); border:0; margin-top:-5.5px;
}
input[type=range]::-moz-range-thumb{width:14px; height:14px; border-radius:50%; background:var(--accent); border:0}
input[type=range]:focus-visible{outline:2px solid var(--accent); outline-offset:3px; border-radius:4px}

/* ---------- phase readout ---------- */
.phasebar{
  max-width:400px; margin:0 auto 18px; padding:10px 15px 12px;
  border-radius:12px; border:1px solid var(--line); background:var(--glass);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px); text-shadow:none;
}
.phase{
  margin:0 0 9px !important; font:500 11.5px/1.3 var(--mono);
  letter-spacing:.03em; color:var(--warm); text-align:left; min-height:1.3em;
}
.phase[data-phase="sealed"]{color:var(--accent)}
.phase[data-phase="refill"]{color:var(--muted)}
.fingers{list-style:none; margin:0; padding:0; display:grid; gap:5px}
.fingers li{display:grid; grid-template-columns:46px 1fr; gap:9px; align-items:center}
.fingers span{font:400 10px/1 var(--mono); letter-spacing:.06em; color:var(--muted); text-align:left}
.bar{display:block; height:3px; border-radius:2px; background:var(--line-hi); overflow:hidden}
.bar b{
  display:block; height:100%; width:100%; background:var(--accent);
  transform:scaleX(0); transform-origin:left center;
}
.fingers li:last-child .bar b{background:var(--pending)}

.herocta{display:flex; gap:10px; align-items:center; justify-content:center; flex-wrap:wrap}
.startbtn{
  display:inline-block; text-decoration:none;
  border:0; border-radius:999px; background:var(--accent); color:#3a2519;
  font:650 15px/1 inherit; padding:14px 26px; cursor:pointer;
  box-shadow:0 10px 30px rgba(232,121,154,.28);
}
.startbtn:hover{background:var(--accent-deep)}
.startbtn--ghost{
  background:var(--glass); color:var(--ink); border:1px solid var(--line-hi);
  box-shadow:none; backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
}
.startbtn--ghost:hover{background:var(--glass-hi)}
.ghostbtn{
  border:1px solid var(--line-hi); background:var(--glass); color:var(--ink);
  border-radius:999px; font:500 13px/1 inherit; padding:13px 18px; cursor:pointer;
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
}
.ghostbtn:hover{background:var(--glass-hi)}
.endcta{display:flex; gap:10px; align-items:center; justify-content:flex-start; margin-top:22px; flex-wrap:wrap}

/* ---------- the board ---------- */
.card--board{max-width:760px}
.boardwrap{
  overflow-x:auto; max-width:100%; border-radius:12px; border:1px solid var(--line);
  background:var(--glass); backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  text-shadow:none;
}
.board{border-collapse:collapse; width:100%; min-width:470px; font-size:14px}
.board th{
  text-align:left; padding:11px 14px; border-bottom:1px solid var(--line-hi);
  font:500 10px/1 var(--mono); letter-spacing:.13em; text-transform:uppercase; color:var(--muted);
  white-space:nowrap;
}
.board td{padding:16px 14px; vertical-align:middle}
.board .c-rank{width:44px; font-family:var(--mono); color:var(--muted); font-variant-numeric:tabular-nums}
.board .c-name b{font-size:17px; letter-spacing:-.01em}
.board .c-link{width:82px}
.board .c-perf{text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums}
.board th.c-perf{text-align:right}
.soon,.pending{
  display:inline-block; font:500 10.5px/1 var(--mono); letter-spacing:.08em;
  border-radius:999px; padding:4px 9px;
}
.soon{color:var(--muted); border:1px solid var(--line-hi)}
.pending{color:var(--pending); border:1px solid rgba(184,116,58,.45); background:rgba(184,116,58,.10)}
.boardnote{margin:14px 0 0 !important; font-size:13px; color:var(--muted)}
.boardnote--quiet{font-size:12px; opacity:.8}

/* ---------- how to enter ---------- */
.entry{list-style:none; margin:6px 0 0; padding:0; display:grid; gap:10px}
.entry li{display:grid; grid-template-columns:32px 1fr; gap:12px; align-items:baseline}
.entry .v{font:600 11px/1.4 var(--mono); letter-spacing:.06em; color:var(--accent)}
.entry .t{font-size:14px; color:var(--muted)}
.entry .t b{color:var(--ink)}

/* ---------- nav ---------- */
.navarrow{
  position:fixed; z-index:31; bottom:calc(var(--safe-b) + 22px);
  width:42px; height:42px; border-radius:50%;
  border:1px solid var(--line-hi); background:var(--glass); cursor:pointer;
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
}
.navarrow::before{
  content:""; position:absolute; inset:0; margin:auto; width:9px; height:9px;
  border-right:2px solid var(--ink); border-bottom:2px solid var(--ink);
}
.navarrow.prev{left:var(--pad)} .navarrow.prev::before{transform:translateX(2px) rotate(135deg)}
.navarrow.next{right:var(--pad)} .navarrow.next::before{transform:translateX(-2px) rotate(-45deg)}
.dots{
  position:fixed; z-index:31; left:50%; transform:translateX(-50%);
  bottom:calc(var(--safe-b) + 34px); display:flex; gap:7px;
}
.dot{width:7px; height:7px; padding:0; border-radius:50%; border:0; cursor:pointer; background:rgba(58,37,25,.22)}
.dot.is-on{background:var(--accent); transform:scale(1.35)}
.swipehint{
  position:fixed; z-index:31; left:50%; transform:translateX(-50%);
  bottom:calc(var(--safe-b) + 52px); margin:0;
  font-size:11px; letter-spacing:.1em; color:var(--muted); pointer-events:none;
  animation:pulse 2.4s ease-in-out infinite;
}
@keyframes pulse{0%,100%{opacity:.45}50%{opacity:.95}}
/* The card title is announced to screen readers on every change. Showing it as
   well would only collide with the swipe hint, which says something else. */
.status{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
}

.footnote{
  position:absolute; left:var(--pad); right:var(--pad);
  bottom:calc(var(--safe-b) + 8px); margin:0; pointer-events:auto;
  font-size:10.5px; line-height:1.5; color:var(--muted); opacity:.78;
  text-align:center; text-shadow:0 1px 12px rgba(247,240,232,.95);
}
.footnote b{font-family:var(--mono); font-weight:500; color:var(--ink)}

/* ---------- portrait ---------- */
@media (max-width:899px){
  .slide--end{flex-direction:column; align-items:stretch; justify-content:flex-end}
  .slide--end .card{margin-bottom:14px; max-height:none}
  .slide--end .footnote{position:static; margin:0 0 calc(var(--safe-b) + 78px)}
  .hero{margin-bottom:calc(var(--safe-b) + 86px); max-width:100%}
  .rig__row{grid-template-columns:88px 1fr 58px}
}

/* ---------- desktop ----------
   The composition splits: copy on the left, hand on the right. js/stage.js
   frames the rig into the right half with the same breakpoint, so the two
   halves are set once here and once there and have to be changed together. */
@media (min-width:900px){
  :root{--card-h:min(70svh,640px)}
  .card{margin-bottom:calc(var(--safe-b) + 96px)}

  .slide{justify-content:flex-start; padding-left:max(var(--pad), 5vw)}
  .hero{text-align:left; max-width:min(46vw,530px); padding:0}
  .hero__sub{margin-left:0; margin-right:0; max-width:430px}
  .rig,.phasebar{margin-left:0; margin-right:0}
  .herocta{justify-content:flex-start}
  .card{max-width:min(48vw,620px)}
  .card--board{max-width:min(52vw,660px)}
  .board{min-width:0}
  /* The footnote sits above the nav row rather than in it, so it never runs
     under the left arrow or the dots; the end card lifts to make room. */
  .slide--end .card{margin-bottom:calc(var(--safe-b) + 138px)}
  .footnote{text-align:left; left:var(--pad); right:auto; max-width:min(52vw,700px);
            bottom:calc(var(--safe-b) + 72px)}
  #scrim{background:
    radial-gradient(34% 46% at 73% 45%, rgba(58,37,25,.12), rgba(58,37,25,0) 72%),
    radial-gradient(120% 85% at 50% 8%, transparent 40%, rgba(239,228,216,.45) 100%),
    linear-gradient(to top, rgba(247,240,232,.96) 0%, rgba(247,240,232,.82) 22%,
                    rgba(247,240,232,.30) 46%, transparent 66%);}
  .dots{bottom:calc(var(--safe-b) + 26px)}
  .navarrow{bottom:calc(var(--safe-b) + 18px)}
  .swipehint{bottom:calc(var(--safe-b) + 46px)}
}

@media (prefers-reduced-motion:reduce){
  .track{transition:none}
  .swipehint{animation:none}
  .boot__track i{transition:none; animation:none; width:34%}
}

:focus-visible{outline:2px solid var(--accent); outline-offset:3px; border-radius:4px}
