/* Components: chrome, kampung stage, feed, give, crates, you, studio.

   House rules for the 8-bit look, applied throughout:
     - no border-radius, anywhere
     - borders are var(--edge) (2px), never 1px
     - shadows are hard offsets, never blurred
     - --display is Press Start 2P and has NO bold cut, so every rule that uses it
       sets font-weight:400; 700 would make the browser smear a fake bold
     - sizes set in --display run about 0.6x what a proportional font would take */

  /* ---------- chrome ---------- */
  /* header and tabs stick as one block: no hardcoded offset to drift out of sync */
  .chrome{position:sticky;top:0;z-index:40}
  .topbar{background:var(--panel);border-bottom:var(--edge) solid var(--hair2)}
  .topbar .in{max-width:1180px;margin:0 auto;padding:12px 22px;display:flex;align-items:center;gap:18px}
  .brand{display:flex;align-items:center;gap:11px;flex:1;min-width:0}
  .brand .sigil{width:42px;height:42px;flex:0 0 42px;background:var(--sunk);border:var(--edge) solid var(--hair2);
    display:grid;place-items:center;overflow:hidden;box-shadow:var(--shadow-sm);padding:2px}
  .brand b{font-family:var(--display);font-size:19px;font-weight:800;line-height:1.3;display:block}
  .purse{display:flex;gap:9px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
  .stat-chip{display:flex;align-items:center;gap:8px;background:var(--sunk);border:var(--edge) solid var(--hair2);padding:5px 12px}
  .stat-chip i{width:16px;height:16px;display:block;background-size:contain;background-repeat:no-repeat;background-position:center}
  .stat-chip i.cry{background-image:url(../assets/art/ui/credit.png);width:20px;height:20px}
  .stat-chip i.coin{background-image:url(../assets/px/ui/coin.png)}
  .stat-chip b{font-family:var(--display);font-size:16px;font-weight:800}
  .stat-chip em{font-style:normal;font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:700}

  nav.tabs{background:var(--bg);border-bottom:var(--edge) solid var(--hair2)}
  nav.tabs .in{max-width:1180px;margin:0 auto;padding:0 22px;display:flex;gap:0;overflow-x:auto}
  nav.tabs button{
    flex:1 1 auto;min-width:max-content;display:flex;align-items:center;justify-content:center;gap:9px;
    background:none;border:0;padding:14px 16px;font-family:var(--display);font-size:14.5px;font-weight:800;
    color:var(--muted);border-bottom:4px solid transparent;white-space:nowrap;
  }
  nav.tabs button .ico{width:17px;height:17px;flex:0 0 17px}
  nav.tabs button:hover{color:var(--ink)}
  nav.tabs button[aria-current="true"]{color:var(--leaf-dk);border-bottom-color:var(--gold)}

  main{max-width:1180px;margin:0 auto;padding:26px 22px 70px;display:flex;flex-direction:column;gap:22px}

  .card{background:var(--panel);border:var(--edge) solid var(--hair2);box-shadow:var(--shadow);
    padding:20px;display:flex;flex-direction:column;gap:14px}
  .card>h3{font-size:17.5px}
  .sub{font-size:15px;color:var(--muted);line-height:1.55}
  .label{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:700}

  .cols{display:grid;grid-template-columns:1.15fr 1fr 1fr;gap:18px}
  /* These are used on their own as well as alongside .cols, and one of them sits
     on a .card (which is display:flex), so each has to declare the grid itself. */
  .cols-left,.cols-right,.cols-two,.cols-one{display:grid;gap:18px}
  .cols-left{grid-template-columns:1.4fr 1fr}
  .cols-right{grid-template-columns:1fr 1.6fr}
  .cols-two{grid-template-columns:1fr 1fr}
  .cols-one{grid-template-columns:1fr}
  /* every side-by-side layout stacks on a phone. These must be classes, not inline
     styles on the element — an inline style outranks any media query. */
  @media (max-width:880px){
    .cols, .cols-left, .cols-right, .cols-two{grid-template-columns:1fr}
  }

  .gl{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);display:inline-block;margin-right:5px;font-weight:700}
  .rar{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;font-weight:400;padding:3px 8px;display:inline-block;border:var(--edge) solid rgba(0,0,0,.28)}
  /* one per box set, matching the border design that box is drawn with */
  .rar.common{color:var(--on-accent);background:var(--r-common)}
  .rar.halloween{color:#fff;background:#6A4C96;border-color:#E88A34}
  .rar.christmas{color:#fff;background:#CE4E52;border-color:#F4EFE4}
  /* --muted is a TEXT colour and too dark to sit under --on-accent; starter gets
     its own pale sage, light enough for dark type in either theme */
  .rar.starter{color:var(--on-accent);background:var(--r-starter)}

  .list{display:flex;flex-direction:column}
  .li{display:flex;gap:12px;align-items:center;padding:11px 0;border-top:var(--edge) solid var(--hair)}
  .li:first-child{border-top:0;padding-top:0}
  .li .thumb{width:46px;height:46px;flex:0 0 46px;background:var(--sunk);display:grid;place-items:center;padding:3px;border:var(--edge) solid var(--hair2)}
  .li .thumb img{max-width:100%;max-height:100%;object-fit:contain}
  .li .m{flex:1;min-width:0}
  .li .m b{font-family:var(--display);font-size:14.5px;font-weight:800;display:block;line-height:1.4}
  .li .m span{font-size:14px;color:var(--muted)}

  /* ---------- buttons ---------- */
  /* a button is a block that sits on a hard shadow and physically drops when pressed */
  .btn{border:var(--edge) solid var(--hair2);background:var(--panel);padding:10px 16px;
    font-family:var(--body);font-weight:600;font-size:15px;white-space:nowrap;
    box-shadow:var(--shadow-sm);transition:transform .06s steps(2),box-shadow .06s steps(2)}
  .btn:hover{background:var(--sunk)}
  .btn:active{transform:translate(2px,2px);box-shadow:0 0 0 var(--hair2)}
  .btn.primary{background:var(--leaf);border-color:var(--leaf-dk);color:var(--on-accent);
    font-family:var(--display);font-weight:800;font-size:16px;padding:13px 20px}
  .btn.primary:hover{background:var(--leaf-dk)}
  .btn.gold{background:var(--gold);border-color:var(--gold-lt);color:var(--on-accent);
    font-family:var(--display);font-weight:800;font-size:16px;padding:13px 20px}
  .btn.gold:hover{background:var(--gold-lt)}
  /* the two drawn buttons from app assets: a cream pill, and a green one with
     leaves either side. border-image keeps their pixel corners at any width. */
  /* The drawings are light in BOTH themes, so their text is a fixed dark ink
     rather than --ink, which turns pale in dark mode. */
  .btn:not(.primary):not(.gold):not(.on){border:8px solid transparent;background:none;box-shadow:none;color:#2E2A1E;
    border-image:url(../assets/art/ui/button_1.png) 16 16 18 16 fill / 8px 8px 9px 8px;padding:4px 10px}
  .btn:not(.primary):not(.gold):not(.on):hover{filter:brightness(.96)}
  .btn.primary{border:0 solid transparent;background:none;box-shadow:none;color:#17280F;
    border-image:url(../assets/art/ui/button_2.png) 34 50 14 50 fill / 17px 25px 7px 25px / 0 10px 0 10px;
    padding:20px 22px 9px;margin:0 10px}
  .btn.primary:hover{background:none;filter:brightness(1.05)}
  .btn.primary:active,.btn:not(.gold):not(.on):active{transform:translate(0,2px);box-shadow:none}
  .btn.wide{width:100%;text-align:center}
  .btn.primary.wide{width:calc(100% - 20px)}
  .btn:disabled{opacity:.42;cursor:not-allowed;box-shadow:none;transform:none}
  .btn.on{background:var(--leaf);border-color:var(--leaf-dk);color:var(--on-accent)}
  @media (prefers-reduced-motion:reduce){ .btn{transition:none} }

  /* ---------- feed ---------- */
  /* one narrow column of polaroids, nothing else. The composer lives in the studio
     overlay (src/studio.js), reached from the floating shutter button. */
  .feed-main{display:flex;flex-direction:column;gap:26px;max-width:540px;width:100%;margin:0 auto}
  .feed-head{gap:8px;padding:14px 18px;align-items:center;text-align:center}
  .feed-head h3{font-size:16px}

  .polaroid{background:var(--panel);border:var(--edge) solid var(--hair2);box-shadow:var(--shadow-lg);
    padding:11px 11px 0;display:flex;flex-direction:column}
  /* cream, not --sunk: the seeded photos are house PNGs with transparent
     backgrounds, and they should sit on the same ground the yard uses */
  .polaroid .shot{position:relative;aspect-ratio:1;overflow:hidden;background:var(--cream);
    border:var(--edge) solid var(--hair2)}
  .polaroid .shot>img{width:100%;height:100%;object-fit:cover}
  /* stickers are stored as percentages of the photo, so one saved post lays out the
     same on a phone and a desktop without re-flattening the image */
  .stk{position:absolute;width:22%;transform-origin:50% 50%;pointer-events:none;
    filter:drop-shadow(2px 2px 0 rgba(16,22,12,.42))}
  .stk img{width:100%;height:auto}

  .polaroid .cap{padding:14px 4px 16px;display:flex;flex-direction:column;gap:9px}
  .polaroid .cap .line{font-size:16px;line-height:1.5;overflow-wrap:anywhere}
  .polaroid .cap .line b{font-family:var(--display);font-size:14.5px;font-weight:800;margin-right:8px}
  .polaroid .foot{display:flex;align-items:center;gap:12px}
  .polaroid .foot .when{font-family:var(--mono);font-size:11px;letter-spacing:.09em;
    text-transform:uppercase;color:var(--muted);margin-left:auto;font-weight:700}
  .cheer{display:inline-flex;align-items:center;gap:8px;background:none;border:0;padding:4px 2px;
    font-family:var(--mono);font-size:13.5px;color:var(--muted);font-weight:700}
  .cheer:active{transform:translateY(1px)}
  .cheer.on{color:var(--rose-ink)}
  .polaroid .chip{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
    background:var(--sunk);border:var(--edge) solid var(--hair2);padding:3px 9px;color:var(--muted);font-weight:700}
  .polaroid .chip.wellness{background:var(--leaf);border-color:var(--leaf-dk);color:var(--on-accent)}

  /* floating shutter, bottom-left, feed only */
  .fab{position:fixed;left:18px;bottom:18px;z-index:60;width:74px;height:64px;
    background:url(../assets/art/ui/icon_camera.png) center/contain no-repeat;border:0;
    filter:drop-shadow(3px 3px 0 rgba(40,50,25,.35));transition:transform .07s steps(2)}
  .fab:hover{transform:translateY(-2px)}
  .fab:active{transform:translateY(2px)}
  @media (prefers-reduced-motion:reduce){ .fab{transition:none} }
  @media (max-width:620px){ .fab{left:12px;bottom:12px;width:62px;height:54px} }

  /* ---------- studio: capture, then sticker, then post ---------- */
  .studio{position:fixed;inset:0;z-index:120;background:var(--bg);display:flex;flex-direction:column}
  .studio-bar{display:flex;align-items:center;gap:12px;padding:12px 16px;
    border-bottom:var(--edge) solid var(--hair2);background:var(--panel)}
  .studio-bar h3{font-size:16px;flex:1}
  .studio-bar .x{background:none;border:0;padding:8px;color:var(--ink);display:grid;place-items:center}
  .studio-body{flex:1;overflow-y:auto;display:flex;flex-direction:column;align-items:center;
    gap:16px;padding:18px 16px 26px}
  .studio-body>*{width:100%;max-width:460px}

  /* the square everything happens inside — live camera, then the captured photo */
  .frame{position:relative;aspect-ratio:1;background:#11150E;overflow:hidden;
    border:var(--edge) solid var(--hair2);box-shadow:var(--shadow)}
  .frame video,.frame>img{width:100%;height:100%;object-fit:cover;display:block}
  .frame video{transform:scaleX(-1)}          /* front cameras are mirrors */
  .frame .stk-layer{position:absolute;inset:0}
  .frame .hint{position:absolute;inset:0;display:grid;place-items:center;text-align:center;
    padding:26px;color:var(--muted);font-size:15px;line-height:1.6;gap:10px}
  /* .hint sets display:grid, which outranks the UA rule for [hidden] */
  .frame .hint[hidden]{display:none}

  .shutter{width:74px;height:74px;margin:0 auto;display:grid;place-items:center;
    background:var(--panel);border:4px solid var(--ink);box-shadow:4px 4px 0 var(--ink);
    color:var(--ink);transition:transform .07s steps(2),box-shadow .07s steps(2)}
  .shutter:active{transform:translate(4px,4px);box-shadow:0 0 0 var(--ink)}
  .shutter i{width:40px;height:40px;background:var(--rose);border:var(--edge) solid var(--ink);display:block}
  @media (prefers-reduced-motion:reduce){ .shutter{transition:none} }
  .studio-row{display:flex;gap:10px;justify-content:center;align-items:center}
  .studio-row .btn{flex:1}

  /* stickers on the edit surface are live: drag the body, drag the corner to size
     and spin, tap the cross to bin it */
  .frame .stk{pointer-events:auto;cursor:grab;touch-action:none}
  .frame .stk.sel{outline:var(--edge) dashed var(--gold);outline-offset:3px}
  .frame .stk.drag{cursor:grabbing}
  .stk .grip{position:absolute;width:20px;height:20px;background:var(--gold);
    border:var(--edge) solid var(--ink);display:none;place-items:center;cursor:nwse-resize}
  .stk .kill{position:absolute;width:20px;height:20px;background:var(--rose);color:var(--on-accent);
    border:var(--edge) solid var(--ink);display:none;place-items:center;cursor:pointer}
  .stk .grip{right:-11px;bottom:-11px}
  .stk .kill{left:-11px;top:-11px}
  .frame .stk.sel .grip,.frame .stk.sel .kill{display:grid}

  .tray{display:flex;gap:8px;overflow-x:auto;padding:10px;background:var(--sunk);
    border:var(--edge) solid var(--hair2)}
  .tray button{flex:0 0 56px;width:56px;height:56px;background:var(--panel);
    border:var(--edge) solid var(--hair2);display:grid;place-items:center;padding:4px}
  .tray button:hover{border-color:var(--gold);background:var(--bg)}
  .tray button img{max-width:100%;max-height:100%;object-fit:contain}

  .cap-input{width:100%;min-height:76px;resize:vertical;border:var(--edge) solid var(--hair2);
    padding:12px 14px;font-family:var(--body);font-size:16px;background:var(--panel);
    color:var(--ink);line-height:1.5;box-shadow:inset var(--shadow-sm)}
  .cap-input::placeholder{color:var(--muted)}

  /* ---------- modal ---------- */
  .modal{position:fixed;inset:0;z-index:80;background:rgba(16,22,12,.72);display:grid;place-items:center;padding:22px}
  .modal .sheet{background:var(--panel);border:var(--edge) solid var(--ink);box-shadow:var(--shadow-lg);
    padding:26px;max-width:400px;width:100%;display:flex;flex-direction:column;gap:15px;align-items:center;text-align:center}
  .modal .sheet img{width:200px;max-width:60vw;height:auto}
  .modal .sheet .doll.big{width:200px;max-width:56vw}
  .modal .sheet .doll img{width:100%;height:100%}
  .modal .sheet img.map-shot{width:100%;max-width:none}
  .modal .sheet h3{font-size:22px;line-height:1.5}
  .pop{animation:pop .45s steps(6,end) both}
  @keyframes pop{from{transform:scale(.5);opacity:0}to{transform:scale(1);opacity:1}}

  /* crate opening: the chest rattles, glows, then bursts into the reveal */
  .opening{position:relative;height:220px;display:grid;place-items:center}
  /* beats .modal .sheet img{width:200px} on specificity */
  .modal .sheet .opening img{width:148px;height:auto;image-rendering:pixelated;
    transform-origin:50% 90%;filter:drop-shadow(0 6px 0 rgba(20,30,10,.22))}
  .opening.live::before{content:"";position:absolute;width:150px;height:150px;
    background:radial-gradient(circle,var(--gold-lt),transparent 66%);
    opacity:0;animation:chest-glow .95s steps(8,end) both}
  .opening.live img{animation:chest-shake .95s steps(1,end) both}
  @keyframes chest-shake{
    0%{transform:rotate(0) scale(1)}
    12%{transform:rotate(-4deg) scale(1.01)}
    24%{transform:rotate(4deg)}
    36%{transform:rotate(-6deg) scale(1.03)}
    48%{transform:rotate(6deg)}
    60%{transform:rotate(-8deg) scale(1.05)}
    72%{transform:rotate(8deg)}
    84%{transform:rotate(-3deg) scale(1.1)}
    100%{transform:rotate(0) scale(1.24)}
  }
  @keyframes chest-glow{from{opacity:0;transform:scale(.35)}to{opacity:.8;transform:scale(1.6)}}
  @media (prefers-reduced-motion:reduce){ .pop{animation:none} }

  .kv{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:14px}
  .kv dt{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);font-weight:700}
  .kv dd{margin:6px 0 0;font-family:var(--display);font-size:22px;font-weight:800}

  .note{background:var(--sunk);border:var(--edge) solid var(--hair);padding:13px 15px;font-size:14.5px;line-height:1.55;color:var(--muted)}
  .note b{color:var(--ink);font-weight:600}

  /* An empty toast is only ~24px tall, so translateY(150%) left 14px of it on
     screen — a pale bar in dark mode. Offset by its own height PLUS a fixed
     distance, and fade it out, so idle state is invisible at any content size. */
  .toast{position:fixed;left:50%;bottom:26px;transform:translate(-50%,calc(100% + 40px));
    z-index:90;background:var(--ink);color:var(--bg);opacity:0;pointer-events:none;
    border:var(--edge) solid var(--ink);padding:12px 20px;font-size:15px;
    box-shadow:4px 4px 0 rgba(0,0,0,.45);
    transition:transform .25s steps(5,end), opacity .2s steps(3);max-width:min(90vw,460px)}
  .toast.up{transform:translate(-50%,0);opacity:1;pointer-events:auto}
  @media (prefers-reduced-motion:reduce){ .toast{transition:none} }

  footer{max-width:1180px;margin:0 auto;padding:0 22px 50px}
  footer p{font-family:var(--mono);font-size:12.5px;letter-spacing:.05em;color:var(--muted);line-height:1.9;font-weight:700}

  /* ---------- sign in ---------- */
  body.signed-out .chrome, body.signed-out main, body.signed-out footer{display:none}
  #gate:empty{display:none}
  #gate{position:fixed;inset:0;z-index:100;background:var(--bg);display:grid;place-items:center;padding:24px;overflow-y:auto}
  .signin{width:100%;max-width:400px;background:var(--panel);border:var(--edge) solid var(--ink);
    box-shadow:var(--shadow-lg);padding:26px;display:flex;flex-direction:column;gap:10px}
  .signin-brand{display:flex;align-items:center;gap:11px;margin-bottom:12px}
  .signin-brand .sigil{width:38px;height:38px;flex:0 0 38px;background:var(--leaf);
    border:var(--edge) solid var(--ink);display:grid;place-items:center;
    font-family:var(--display);font-weight:800;color:var(--on-accent);font-size:19px;box-shadow:var(--shadow-sm)}
  .signin-brand .sigil img{width:100%;height:100%;object-fit:contain;image-rendering:auto;padding:3px}
  .signin-brand b{font-family:var(--display);font-size:19px;font-weight:800;line-height:1.3;display:block}
  .signin-brand span span{font-family:var(--mono);font-size:11px;letter-spacing:.13em;
    text-transform:uppercase;color:var(--muted);font-weight:700}
  .signin h1{font-size:20.5px;line-height:1.5}
  .signin label{font-family:var(--mono);font-size:11px;letter-spacing:.12em;
    text-transform:uppercase;color:var(--muted);margin-top:8px;font-weight:700}
  .signin input{width:100%;border:var(--edge) solid var(--hair2);padding:12px 13px;
    font-family:var(--body);font-size:17px;background:var(--sunk);color:var(--ink);
    box-shadow:inset var(--shadow-sm)}
  .signin input:focus-visible{outline:var(--edge) solid var(--sky);outline-offset:1px}
  .signin-error{min-height:18px;font-size:14px;color:var(--rose-ink);font-weight:600}
  .signin .btn{margin-top:6px}
  .signin-demo{margin-top:12px;padding-top:12px;border-top:var(--edge) solid var(--hair);
    font-size:13px;line-height:1.7;color:var(--muted)}
  /* NOT --mono: Silkscreen's lowercase reads as small caps, and this password is
     case-sensitive — people would type KAMPUNG2026 and be locked out. */
  .signin-demo code{font-family:var(--body);font-weight:700;color:var(--ink);
    background:var(--sunk);padding:2px 7px;border:1px solid var(--hair2)}

  /* ---------- phone ---------- */
  @media (max-width:620px){
    main{padding:16px 14px 56px;gap:16px}
    .topbar .in{padding:10px 14px;gap:10px}
    nav.tabs .in{padding:0 6px}
    /* five labels in Press Start 2P will not fit across a phone beside an icon,
       so stack them instead of shrinking the type into illegibility */
    nav.tabs button{padding:9px 4px;font-size:8px;flex-direction:column;gap:5px}
    nav.tabs button .ico{width:19px;height:19px;flex:0 0 19px}
    .card{padding:15px}
    .track{padding:16px 15px}
    .hero-spirit{gap:12px}
    .hero-spirit .port{width:112px;height:112px;flex:0 0 112px}
    .brand b{font-size:10.5px}
    .stat-chip{padding:5px 9px}
    .stat-chip em{display:none}
    .signin{padding:22px}
    .feed-main{gap:20px}
    .bub{font-size:12px;padding:5px 8px;max-width:58vw}
  }

  /* ---------- page icons ---------- */
  /* drawn in colour, so these are images rather than currentColor masks */
  .ico.home,.ico.donate,.ico.classes,.ico.photos,.ico.me{-webkit-mask:none;mask:none;
    background:center/contain no-repeat;width:28px;height:26px;image-rendering:auto}
  .ico.home   {background-image:url(../assets/art/ui/icon_home.png)}
  .ico.donate {background-image:url(../assets/art/ui/icon_donate.png)}
  .ico.classes{background-image:url(../assets/art/ui/icon_classpass.png)}
  .ico.photos {background-image:url(../assets/art/ui/photo.png)}
  .ico.me     {background-image:url(../assets/art/ui/icon_custom.png)}
  .ico.debug  {--i:url(../assets/px/ui/star.png)}
  .dbg-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:10px}

  /* ---------- the ghost ---------- */
  /* One character, built from stacked layers that all share a 320x380 canvas.
     Nothing positions a hat: every layer is already drawn where it belongs.
     The art is ~8px per drawn pixel and always shown SMALLER than that, so it is
     filtered normally: nearest-neighbour would drop outline pixels at random. */
  .doll{position:relative;width:100%;aspect-ratio:320/380}
  .doll img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
    image-rendering:auto}
  .doll.big{width:210px;max-width:46vw;margin:0 auto}
  .doll.chip{width:30px;flex:0 0 30px}
  .sigil .doll{width:100%;height:100%}

  /* ---------- home: the yard ---------- */
  .yard{display:flex;flex-direction:column;background:var(--cream);
    border:var(--edge) solid var(--hair2);box-shadow:var(--shadow)}
  /* the total, as a bar above the house */
  .hp{padding:14px 16px 10px;border-bottom:var(--edge) solid var(--cream-line)}
  .hp-top{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:9px;color:#3A3626}
  .hp-top b{font-family:var(--display);font-size:22px;font-weight:800}
  .hp-top span{font-size:14px;opacity:.72}
  .hp-bar{position:relative;height:18px;background:#E3D9BC;border:var(--edge) solid #B9AA85;overflow:hidden}
  .hp-bar i{display:block;height:100%;background:linear-gradient(90deg,var(--leaf),var(--gold));
    transition:width .5s steps(14,end)}
  /* a notch per milestone, so the bar reads as five stages not one long crawl */
  .hp-bar u{position:absolute;top:0;bottom:0;width:var(--edge);background:#B9AA85;transform:translateX(-1px)}
  .hp-bar u.on{background:rgba(255,255,255,.65)}
  @media (prefers-reduced-motion:reduce){ .hp-bar i{transition:none} }

  /* the field is a painted 320x180 pixel-art scene (tools/spirit.py, build_field),
     scaled up nearest-neighbour; the horizon sits at 39% of its height */
  /* a dark forest clearing, painted at 320x200 (tools/spirit.py, build_forest)
     and scaled up nearest-neighbour; the house stands at the top of it */
  .scene{position:relative;width:100%;aspect-ratio:16/10;min-height:300px;overflow:hidden;
    isolation:isolate;background:#22322A url(../assets/art/ui/forest.png) center bottom/cover no-repeat;
    image-rendering:pixelated}
  /* isolate: roamers carry z-index up to ~100 for depth sorting, and would
     otherwise paint over the sticky header (z-index 40) when the page scrolls */
  @media (max-width:740px){ .scene{aspect-ratio:4/5;min-height:0} }
  /* the house stands at the top of the clearing and grows with each milestone */
  .house{position:absolute;left:50%;top:3%;transform:translateX(-50%);
    width:var(--hw,46%);height:auto;z-index:5;image-rendering:auto}
  .house.st1{--hw:26%} .house.st2{--hw:27%} .house.st3{--hw:30%} .house.st4{--hw:32%} .house.st5{--hw:35%}
  /* smaller on a phone, or the spirits have nowhere to stand */
  @media (max-width:740px){ .house.st1{--hw:40%} .house.st2{--hw:42%} .house.st3{--hw:46%} .house.st4{--hw:48%} .house.st5{--hw:52%} }

  /* snow, once the final milestone is done */
  .snow{position:absolute;inset:0;pointer-events:none;z-index:200;overflow:hidden}
  .snow i{position:absolute;top:-4%;background:#F4F6FF;opacity:.85;
    animation-name:snowfall,snowsway;animation-timing-function:linear,ease-in-out;
    animation-iteration-count:infinite,infinite;animation-direction:normal,alternate}
  @keyframes snowfall{to{top:104%}}
  @keyframes snowsway{from{margin-left:-6px}to{margin-left:6px}}
  @media (prefers-reduced-motion:reduce){ .snow{display:none} }

  /* confetti, when a milestone is reached */
  .confetti{position:fixed;inset:0;pointer-events:none;z-index:300;overflow:hidden}
  .confetti i{position:absolute;top:-20px;animation:confetti linear forwards}
  @keyframes confetti{
    0%{transform:translate(0,0) rotate(0)}
    100%{transform:translate(var(--dx),110vh) rotate(var(--spin))}
  }

  /* Hidden spirits: one per house upgrade */
  .hidden-spirits{gap:12px}
  .hs-list{display:flex;flex-direction:column;gap:10px}
  .hs{display:flex;align-items:center;gap:14px;padding:10px 12px;border:var(--edge) solid var(--hair);background:var(--sunk)}
  .hs-house{width:64px;height:56px;object-fit:contain;flex:0 0 64px}
  .hs.locked .hs-house{filter:grayscale(1) brightness(.75);opacity:.6}
  .hs-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
  .hs-text b{font-size:17px;font-weight:800}
  .hs.locked .hs-text b{color:var(--muted)}
  .hs-badge{width:38px;height:38px;flex:0 0 38px;display:grid;place-items:center;font-size:20px;
    border:var(--edge) solid var(--hair2);background:var(--panel);font-weight:800;color:var(--muted)}
  .hs.out{border-color:var(--leaf);background:color-mix(in oklab,var(--leaf) 12%,var(--panel))}

  .roamer{position:absolute;will-change:transform;transform-origin:bottom center}
  .roamer .doll{filter:drop-shadow(0 2px 0 rgba(90,76,40,.28))}

  /* what the ghosts say, and the bubble that opens someone's post */
  .bub{position:absolute;left:50%;bottom:100%;transform:translate(-50%,-6px);
    background:var(--panel);border:var(--edge) solid var(--ink);color:var(--ink);
    padding:6px 10px;font-size:13px;line-height:1.35;
    width:max-content;max-width:min(62vw,190px);text-align:center;
    box-shadow:2px 2px 0 rgba(60,50,20,.30);z-index:2}
  .bub::after{content:"";position:absolute;left:50%;top:100%;margin-left:-5px;
    border:5px solid transparent;border-top-color:var(--ink)}
  /* chatter is drawn in the speaker's set: the plain cream box, or the Halloween
     and Christmas ones with their ornaments hung outside the bubble (outset) */
  .bub.common,.bub.halloween,.bub.christmas{background:none;box-shadow:none;border:2px solid transparent;
    padding:3px 8px;color:#2E2A1E;filter:drop-shadow(2px 2px 0 rgba(60,50,20,.25))}
  .bub.common{border-image:url(../assets/art/ui/textbox_common.png) 24 fill / 12px / 4px}
  .bub.halloween{border-image:url(../assets/art/ui/textbox_hween.png) 62 64 44 74 fill / 31px 32px 22px 37px / 19px 10px 10px 12px}
  .bub.christmas{border-image:url(../assets/art/ui/textbox_xmas.png) 50 66 50 64 fill / 25px 33px 25px 32px / 13px 6px 10px 14px}
  .bub.common::after,.bub.halloween::after,.bub.christmas::after{border-top-color:#2A1E1E;margin-top:4px}
  .bub.halloween::after{margin-top:9px} .bub.christmas::after{margin-top:9px}
  .bub[data-edge="left"]{left:0;transform:translate(-12%,-6px)}
  .bub[data-edge="right"]{left:auto;right:0;transform:translate(12%,-6px)}
  .bub[data-edge="left"]::after{left:24%} .bub[data-edge="right"]::after{left:76%}
  .bub.post{cursor:pointer;display:flex;align-items:center;gap:7px;white-space:nowrap;
    background:var(--gold);border-color:var(--ink);color:var(--on-accent);
    font-family:var(--mono);font-size:12.5px;letter-spacing:.04em;text-transform:uppercase;font-weight:700}
  .bub.post:hover{background:var(--gold-lt)}
  .bub.post .ico{width:15px;height:11px}
  /* on a phone the name tags must not cover the spirits (this has to come after
     the rule above to win) */
  @media (max-width:620px){
    .bub.post{font-size:9.5px;padding:2px 6px;gap:4px}
    .bub.post .ico{width:11px;height:8px}
  }

  .home-next{align-items:flex-start;gap:10px}
  .home-next h3{font-size:17.5px;line-height:1.5;color:var(--muted)}
  .home-next h3 span{color:var(--ink)}

  /* ---------- donate ---------- */
  .give-top .kv{gap:18px}
  .give-top dd{font-size:16px}
  .rate{gap:12px}
  .rate-eq{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
  .rate-eq b{font-family:var(--display);font-size:20.5px;font-weight:800}
  .rate-eq em{font-style:normal;font-family:var(--display);font-size:19px;color:var(--muted);font-weight:800}
  /* NOT .coin: the icon inside carries that class too, and this selector would
     then repaint the icon's own background over it */
  .rate-eq .coin-tile{display:grid;place-items:center;width:34px;height:34px;
    background:var(--sunk);border:var(--edge) solid var(--hair2)}
  .rate-eq .coin-tile .ico{width:20px;height:20px}

  .amts{display:flex;gap:10px;flex-wrap:wrap}
  .amts button{display:flex;flex-direction:column;align-items:center;gap:4px;
    background:var(--sunk);border:var(--edge) solid var(--hair2);padding:12px 18px;
    box-shadow:var(--shadow-sm)}
  .amts button b{font-family:var(--display);font-size:17.5px;font-weight:800}
  .amts button span{font-family:var(--mono);font-size:12px;color:var(--muted);font-weight:700}
  .amts button:active{transform:translate(2px,2px);box-shadow:none}
  .amts button[aria-pressed="true"]{border-color:var(--gold);background:var(--gold)}
  .amts button[aria-pressed="true"] b,
  .amts button[aria-pressed="true"] span{color:var(--on-accent)}

  .miles{display:flex;flex-direction:column}
  .mile{display:flex;gap:12px;align-items:center;padding:12px 0;border-top:var(--edge) solid var(--hair)}
  .mile:first-child{border-top:0;padding-top:0}
  .mile .tick{width:26px;height:26px;flex:0 0 26px;display:grid;place-items:center;
    border:var(--edge) solid var(--hair2);background:var(--sunk);color:var(--muted)}
  .mile.done .tick{background:var(--leaf);border-color:var(--leaf-dk);color:var(--on-accent)}
  .mile .m{flex:1;min-width:0}
  .mile .m b{font-family:var(--display);font-size:14.5px;font-weight:800;display:block;line-height:1.45}
  .mile .m span{font-size:14px;color:var(--muted)}
  .mile .at{text-align:right;font-family:var(--mono);font-size:13.5px;white-space:nowrap;font-weight:700}
  .mile .at em{display:block;font-style:normal;font-size:10px;color:var(--muted);margin-top:3px}
  .mile.done .at em{color:var(--leaf-dk)}
  .mile.todo{opacity:.72}

  /* ---------- classes ---------- */
  .classes{display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:14px}
  .klass{background:var(--panel);border:var(--edge) solid var(--hair2);box-shadow:var(--shadow);
    padding:16px;display:flex;flex-direction:column;gap:9px}
  .klass.joined{border-color:var(--leaf);background:color-mix(in oklab,var(--leaf) 10%,var(--panel))}
  .klass .when b{display:block;font-family:var(--mono);font-size:13.5px;letter-spacing:.04em;
    text-transform:uppercase;color:var(--leaf-dk);font-weight:700}
  .klass .when span{font-family:var(--mono);font-size:12.5px;color:var(--muted);font-weight:700}
  .klass .when{display:flex;flex-direction:column;gap:1px}
  .klass .when span{display:block}
  .klass-note{font-size:12.5px;color:var(--rose-ink);font-weight:700}
  .klass h4{font-family:var(--display);font-size:16px;font-weight:800;line-height:1.5}
  .klass-foot{margin-top:auto;padding-top:6px;display:flex;align-items:center;
    justify-content:space-between;gap:10px}
  .price{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);font-size:13.5px;font-weight:700}
  .price .ico{width:15px;height:15px}

  /* ---------- me ---------- */
  .hero-me{align-items:center;text-align:center;gap:16px}
  .hero-me .hero-meta{width:100%}
  .hero-me h3{font-size:19px}
  .slot-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}

  /* each set's box sits in its own drawn border, so you tell them apart at a glance */
  .boxes{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px}
  .box{background:var(--panel);padding:14px;display:flex;flex-direction:column;gap:10px;
    text-align:center;border:var(--edge) solid var(--hair2)}
  .box h4{font-family:var(--display);font-size:16px;font-weight:800;line-height:1.5}
  .box-frame{aspect-ratio:1;width:min(100%,220px);margin:0 auto;display:grid;place-items:center;
    background:center/contain no-repeat}
  .box-frame img{width:58%;height:auto;image-rendering:auto}
  .box .btn{margin-top:auto}
  .grid-coll{display:grid;grid-template-columns:repeat(auto-fill,minmax(124px,1fr));gap:10px}
  .cell{background:var(--panel);border:var(--edge) solid var(--hair2);padding:9px;
    display:flex;flex-direction:column;gap:6px;align-items:center;text-align:center}
  .cell.eq{border-color:var(--leaf);background:color-mix(in oklab,var(--leaf) 10%,var(--panel))}
  .cell .fr{width:100%;background:var(--sunk);display:grid;place-items:center;
    border:var(--edge) solid var(--hair);padding:4px}
  .cell .doll.mini{width:100%}
  /* each accessory in its box's border: Everyday, Halloween or Christmas */
  .cell[class*="set-"] .fr{aspect-ratio:1;background:center/contain no-repeat;padding:0;border:0;
    display:grid;place-items:center}
  .cell[class*="set-"] .doll.mini{width:58%}
  .cell.set-common .fr{background-image:url(../assets/art/ui/border_common.png)}
  .cell.set-halloween .fr{background-image:url(../assets/art/ui/border_hween.png)}
  .cell.set-christmas .fr{background-image:url(../assets/art/ui/border_xmas.png)}
  .cell.locked[class*="set-"] .fr{opacity:.85}
  .cell.locked{cursor:not-allowed}
  /* locked items still show their silhouette, so you can see what is in the set */
  .cell.locked .doll{filter:grayscale(1) brightness(.72) opacity(.55)}
  .cell.locked b,.cell.locked .tier{opacity:.6}
  .cell b{font-family:var(--display);font-size:12.5px;font-weight:800;line-height:1.5}
  .cell .tier{font-family:var(--mono);font-size:11px;color:var(--muted);font-weight:700}

  .reveal-name{font-family:var(--display);font-size:19px;font-weight:800;line-height:1.5}
  /* the box rattles inside its set's border (keyframes: chest-shake, above). The frame
     image is set inline: a url() inside a custom property resolves against this
     stylesheet's folder, not the page. */
  .opening{background:center/contain no-repeat;width:230px;height:230px;margin:0 auto}
  .modal .sheet .opening img{width:120px;image-rendering:auto}

  /* ---------- photo bylines ---------- */
  .polaroid .by{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
  .polaroid .by b{font-family:var(--display);font-size:14.5px;font-weight:800}
  .polaroid .by .when{margin-left:auto;font-family:var(--mono);font-size:11px;
    letter-spacing:.09em;text-transform:uppercase;color:var(--muted);font-weight:700}

  /* ---------- gate extras ---------- */
  .linkish{background:none;border:0;padding:0;font:inherit;color:var(--leaf-dk);
    text-decoration:underline;text-underline-offset:3px}
  .signin-alt{margin-top:12px;font-size:14px;color:var(--muted)}
  .signin .sub{font-size:14.5px;color:var(--muted);line-height:1.55}
  .tnc{display:flex;gap:10px;align-items:flex-start;margin-top:12px;font-size:14px;line-height:1.5}
  .tnc input{width:20px;height:20px;flex:0 0 20px;margin-top:2px;accent-color:var(--leaf)}
  .codebox{font-family:var(--mono);font-size:29px;letter-spacing:.42em;text-align:center;font-weight:700}
  .demo-code{margin-top:12px;padding:10px 12px;background:var(--gold);color:var(--on-accent);
    border:var(--edge) solid var(--gold-ink);font-size:13px;line-height:1.5}
  .demo-code code{font-family:var(--mono);font-weight:700;letter-spacing:.1em}
  .modal .sheet.terms{text-align:left;align-items:stretch;max-width:560px;max-height:86vh}
  .terms-body{overflow-y:auto;max-height:62vh;padding-right:6px;font-size:14px;line-height:1.5}
  .terms-body h4{font-size:15px;margin:14px 0 4px}
  .terms-body p{margin:0 0 8px}
  .terms-body ul{margin:0 0 8px;padding-left:20px}
  .terms-body li{margin:2px 0}
  .terms-body table{width:100%;border-collapse:collapse;margin:4px 0 10px;font-size:13px}
  .terms-body th,.terms-body td{border:1px solid var(--hair);padding:6px 8px;text-align:left;vertical-align:top}
  .terms-body th{background:var(--sunk)}

  /* ---------- tutorial ---------- */
  /* Deliberately plain and large: the brief asks for a tour that assumes the
     reader has not used an app like this before. */
  .tutor{position:fixed;inset:0;z-index:110;background:rgba(16,24,14,.62);
    display:flex;align-items:center;justify-content:center;padding:18px}
  .tutor-card{width:100%;max-width:470px;background:var(--panel);
    border:var(--edge) solid var(--ink);box-shadow:var(--shadow-lg);
    padding:24px;display:flex;flex-direction:column;gap:12px;text-align:center}
  /* centred card and title; the body reads left-aligned */
  .tutor-card{text-align:center}
  .tutor-card h3{font-size:22px;line-height:1.35;display:flex;align-items:center;justify-content:center;gap:6px;flex-wrap:wrap}
  .tutor-sub{font-weight:800;color:var(--leaf-dk);font-size:16px}
  .tutor-body{text-align:left;display:flex;flex-direction:column;gap:10px;max-height:46vh;overflow-y:auto}
  .tutor-body p{font-size:15.5px;line-height:1.6}
  .tut-emoji{width:22px;height:22px;display:inline-block;image-rendering:auto}
  .tutor-dots{display:flex;gap:6px;justify-content:center;margin-top:4px}
  .tutor-dots i{width:10px;height:10px;background:var(--hair2)}
  .tutor-dots i.on{background:var(--leaf)}
  .tutor-row{display:flex;gap:10px}
  .tutor-row .btn{flex:1;justify-content:center;text-align:center}
