  /* ---------- side decorations (wide screens only) ---------- */
  .side-coffee,.side-shelf{display:none; position:fixed; top:90px; z-index:5;}

  /* ---------- mobile hero band ----------
     Below 1180px the side gutters don't exist, so the ambient cup + shelf move into
     a band at the top of <main> (initMobileWidgets() reparents the SAME nodes — the
     WebGL canvases keep their contexts). The band scrolls away with the content, so
     nothing ever overlaps the lists. Desktop (≥1180px) is untouched. */
  #mobileWidgets{display:none;}
  @media (max-width:1179px){
    #mobileWidgets{display:flex; align-items:flex-end; justify-content:space-evenly;
      gap:6px; padding:8px 4px 0; height:200px; overflow:hidden;}
    #mobileWidgets .side-coffee, #mobileWidgets .side-shelf{
      display:block; position:static; width:auto; z-index:auto;}
    #mobileWidgets .cup3d{max-width:120px; max-height:146px;}
    /* the shelf renders tall for its width; scale it into the band (bottom-anchored
       so the crop, if any, eats empty space above the plant, not the shelves) */
    #mobileWidgets .side-shelf{width:150px; min-height:0;
      transform:scale(.62); transform-origin:bottom center;}
    /* touch: the ⠿ move handle and hover hints are desktop affordances */
    #mobileWidgets .cup-move, #mobileWidgets .coffee-note, #mobileWidgets .shelf-hint{display:none;}
    /* designer chips: the touch replacement for the hover hints (tap the object works too) */
    #mobileWidgets{position:relative;}
    #mobileWidgets .mw-chip{display:flex; align-items:center; justify-content:center;
      position:absolute; bottom:8px; width:34px; height:34px; padding:0;
      border:1px solid var(--line); border-radius:50%; background:var(--card);
      font-size:16px; line-height:1; box-shadow:var(--e1); cursor:pointer;}
    #mobileWidgets .mw-chip.cup{left:10px;}
    #mobileWidgets .mw-chip.shelf{right:10px;}
    /* horizontal drag rotates the shelf; keep vertical swipes scrolling the page */
    #mobileWidgets .side-shelf canvas{touch-action:pan-y;}
    #mobileWidgets .cup3d canvas.cup-gl{touch-action:pan-y;}
  }
  @media (min-width:1180px){
    /* wide enough for the near-full-size 3D cup (see .cup3d.side) — a hero object,
       not a thumbnail */
    .side-coffee{display:block; left:24px; width:210px;}
    .side-shelf{display:block; right:16px; width:190px;}
  }
  .mug-wrap{margin:30px auto 0; width:74px; position:relative;}
  .mug{width:64px; height:52px; background:linear-gradient(160deg,var(--accent),var(--accent-dark));
    border-radius:6px 6px 16px 16px; position:relative; box-shadow:0 4px 10px rgba(58,40,23,.25);}
  .mug::after{content:""; position:absolute; right:-16px; top:8px; width:20px; height:24px;
    border:5px solid var(--accent-dark); border-radius:0 12px 12px 0; border-left:none;}
  .saucer{width:84px; height:10px; background:var(--line); border-radius:50%; margin:4px auto 0;}
  /* steam: soft vapor puffs — layered radial-gradient clouds that billow upward on two
     alternating serpentine paths (real vapor never rises in straight lines), growing
     and thinning as they climb. Density/speed follow the tea's TEMPERATURE via t0..t3:
       t0 iced (none) · t1 warm (2 lazy puffs) · t2 hot (3) · t3 steaming (4, fast) */
  .steam{position:absolute; top:-64px; left:10px; width:60px; height:64px;}
  /* steam color: auto per theme, or the reader's own pick (--steam-c inline) */
  .steam{--steam-auto:#a89478;}
  body.dark .steam{--steam-auto:#f0e6d4;}
  .steam i{position:absolute; bottom:0; width:22px; height:22px; border-radius:50%; opacity:0;
    background:radial-gradient(circle at 45% 48%,
      color-mix(in srgb, var(--steam-c, var(--steam-auto)) 50%, transparent) 0%,
      color-mix(in srgb, var(--steam-c, var(--steam-auto)) 30%, transparent) 35%,
      color-mix(in srgb, var(--steam-c, var(--steam-auto)) 10%, transparent) 58%,
      transparent 74%);
    filter:blur(4px); animation:puffA 4.2s ease-out infinite;}
  .steam i:nth-child(1){left:2px;}
  .steam i:nth-child(2){left:18px; animation-delay:1.1s; width:27px; height:27px; animation-name:puffB;}
  .steam i:nth-child(3){left:32px; animation-delay:2.2s;}
  .steam i:nth-child(4){left:10px; animation-delay:3.2s; width:18px; height:18px; animation-name:puffB;}
  .steam.t0 i{display:none;}
  .steam.t1 i{--steam-op:.4; animation-duration:6s;}
  .steam.t1 i:nth-child(n+3){display:none;}
  .steam.t2 i{--steam-op:.7;}
  .steam.t2 i:nth-child(4){display:none;}
  .steam.t3 i{--steam-op:.95; animation-duration:2.8s; filter:blur(4.5px);}
  @keyframes puffA{
    0%{transform:translate(0,14px) scale(.3); opacity:0;}
    12%{opacity:calc(var(--steam-op,.7)*.9);}
    30%{transform:translate(5px,-6px) scale(.65);}
    50%{transform:translate(-4px,-24px) scale(1); opacity:calc(var(--steam-op,.7)*.6);}
    72%{transform:translate(6px,-44px) scale(1.45); opacity:calc(var(--steam-op,.7)*.3);}
    100%{transform:translate(-3px,-66px) scale(2.1); opacity:0;}}
  @keyframes puffB{
    0%{transform:translate(0,14px) scale(.3); opacity:0;}
    12%{opacity:calc(var(--steam-op,.7)*.85);}
    30%{transform:translate(-5px,-8px) scale(.7);}
    50%{transform:translate(5px,-26px) scale(1.05); opacity:calc(var(--steam-op,.7)*.55);}
    72%{transform:translate(-6px,-46px) scale(1.5); opacity:calc(var(--steam-op,.7)*.28);}
    100%{transform:translate(4px,-68px) scale(2.2); opacity:0;}}
  .coffee-note{text-align:center; font-size:11px; color:var(--muted); margin-top:14px; font-style:italic;}

  .shelf-title{font-size:10.5px; font-weight:700; letter-spacing:.6px; text-transform:uppercase;
    color:var(--muted); text-align:center; margin-bottom:6px;}
  /* ---------- 3D bookshelf (WebGL — see js/shelf3d.js) ---------- */
  .shelf3d-wrap{position:relative; width:100%; aspect-ratio:0.5; }
  .shelf3d-wrap canvas.shelf-gl{position:absolute; inset:0; width:100%; height:100%; display:block; touch-action:manipulation;}
  .shelf3d-wrap .decor-layer{position:absolute; inset:0; pointer-events:none;}
  .shelf3d-wrap .decor-layer .decor-free{pointer-events:auto;}
  .shelf-arrow.s3d{position:absolute; right:2px;}
  .shelf-arrow.s3d[data-r="0"]{top:22%;}
  .shelf-arrow.s3d[data-r="1"]{top:50%;}
  .shelf-arrow.s3d[data-r="2"]{top:78%;}
  .shelf-empty.s3d{position:absolute; top:46%; left:0; right:0;}
  .shelf-row{background:linear-gradient(180deg,#8a6a48,#6e5237); border-radius:3px;
    padding:6px 6px 0; margin-bottom:6px; min-height:66px; display:flex; align-items:flex-end;
    gap:2px; flex-wrap:nowrap; overflow:hidden; box-shadow:inset 0 -6px 8px rgba(0,0,0,.25);}
  .shelf-row .decor{font-size:22px; line-height:1; margin:0 1px 6px;}
  .spine{width:11px; border-radius:2px 2px 0 0; box-shadow:inset -2px 0 3px rgba(0,0,0,.25);
    margin-bottom:6px; flex:none;}
  .shelf-board{height:7px; background:#4e3921; border-radius:2px; margin:-6px 0 8px; box-shadow:0 3px 5px rgba(0,0,0,.3);}
  .shelf-empty{font-size:11px; color:var(--muted); text-align:center; padding:20px 6px;}

  /* ---------- pick plate scene: a set table ----------
     A woven placemat grounds everything (plate, cutlery, cup) on one surface,
     the plate gets a glaze highlight + embossed ring, the cup sits ON the mat —
     one composed table setting instead of objects floating in space. */
  .plate-scene{position:relative; width:min(380px, 94%); margin:0 auto 12px; height:300px;}
  .plate-scene::before{content:""; position:absolute; left:50%; bottom:-8px; transform:translateX(-50%);
    width:118%; height:120px; border-radius:50%;
    background:
      repeating-linear-gradient(90deg, rgba(58,40,23,.05) 0 3px, transparent 3px 9px),
      repeating-linear-gradient(0deg, rgba(58,40,23,.04) 0 3px, transparent 3px 9px),
      radial-gradient(ellipse at 50% 50%, var(--accent-soft) 55%, transparent 74%);
    opacity:.9;}
  body.dark .plate-scene::before{opacity:.55;}
  .plate{position:absolute; bottom:0; left:50%; transform:translateX(-50%); width:290px; height:84px; border-radius:50%;
    background:radial-gradient(ellipse at 50% 38%, #ffffff 34%, #f2ead9 55%, #ddd2bd 76%, #c4b599 96%);
    box-shadow:0 14px 26px rgba(58,40,23,.35), inset 0 -10px 18px rgba(150,130,100,.25);}
  body.dark .plate{background:radial-gradient(ellipse at 50% 38%, #ece2d0 34%, #cfc4ae 62%, #96876c 100%);
    box-shadow:0 14px 30px rgba(0,0,0,.55), inset 0 -10px 18px rgba(120,100,70,.3);}
  .plate::before{content:""; position:absolute; left:18%; top:12%; width:38%; height:26%;
    border-radius:50%; background:radial-gradient(ellipse, rgba(255,255,255,.85), transparent 70%);
    transform:rotate(-6deg);} /* glaze catching the light */
  .plate::after{content:""; position:absolute; inset:12px 38px; border-radius:50%;
    border:3px solid rgba(150,130,100,.35); box-shadow:inset 0 4px 8px rgba(120,100,70,.2);}
  .plate-scene .prcov{position:absolute; left:50%; transform:translateX(-50%); bottom:30px;
    margin:0; display:none; width:180px; height:265px; font-size:54px;
    box-shadow:0 16px 32px rgba(58,40,23,.5);}
  body.dark .plate-scene .prcov{box-shadow:0 14px 30px rgba(0,0,0,.6);}
  .fork{position:absolute; left:2px; bottom:20px; font-size:48px; transform:rotate(-10deg);
    filter:drop-shadow(0 6px 6px rgba(58,40,23,.4));}
  .plate-cup{position:absolute; right:-14px; bottom:8px; width:190px;
    transform:scale(.38); transform-origin:bottom right; pointer-events:none;}

  /* ---------- 3D teacup (customizable, WebGL — see js/cup3d.js) ---------- */
  .cup3d{position:relative; width:190px; height:230px; margin:0 auto;}
  .cup3d canvas.cup-gl{position:absolute; inset:0; width:100%; height:100%; touch-action:none; cursor:grab; display:block;}
  .cup3d .steam{position:absolute; top:-24px; left:50%; transform:translateX(-50%); z-index:2; pointer-events:none;}
  .cup3d.side{transform:scale(.95); transform-origin:top center; margin-bottom:-14px;}
  /* drag handle for repositioning the whole side-cup block. Appears on hover,
     then fades away ~2.5s after the pointer leaves — invisible when not in use. */
  .cup-move{position:absolute; top:-6px; left:2px; z-index:6; border:none; background:none;
    color:var(--muted); font-size:18px; cursor:grab; padding:4px 8px; opacity:0;
    touch-action:none; border-radius:8px; transition:opacity .5s ease 2.5s;}
  .side-coffee:hover .cup-move,
  .cup-move.show{opacity:.85; transition-delay:0s; transition-duration:.15s;}
  .cup-move:active{cursor:grabbing; background:var(--accent-soft);}
  .shelf3d-wrap .arrow-layer{position:absolute; inset:0; pointer-events:none;}
  .shelf3d-wrap .arrow-layer .shelf-arrow{pointer-events:auto; right:2px;}
  /* the open flat canvas: the unwrapped 520x360 drawing surface, 1:1 with the texture */
  .mini.on{background:var(--accent); color:#fff;}
  /* Designer preview is deliberately much bigger than the 190x230 base .cup3d used
     elsewhere (side panel / plate): this is the one place people actually draw on the
     cup, so more on-screen pixels = easier painting AND easier for us to spot geometry/
     texture problems. Responsive (min() + aspect-ratio) so it still fits narrow phones
     instead of overflowing the sheet. */
  #cupPreview{width:min(360px, 88vw); aspect-ratio:6/7; height:auto; margin:0 auto;}
  .swatches{display:flex; flex-wrap:wrap; gap:8px; margin-top:6px;}
  .swatches button{width:34px; height:34px; border-radius:50%; border:3px solid var(--line); cursor:pointer;}
  .swatches button.on{border-color:var(--ink);}
  .swatches input[type=color]{width:38px; height:38px; border:none; background:none; cursor:pointer; padding:0;}
  .brush-row{display:flex; align-items:center; gap:10px; margin-top:6px; flex-wrap:wrap;}

  /* ---------- 3D front-facing bookshelf ---------- */
  :root{--wood1:#96754f; --wood2:#7a5c3a; --wood3:#6e5237; --wood4:#5a4227; --wood5:#54390f; --wood6:#3d2a12;}
  .shelf-row{position:relative; display:flex; gap:6px; align-items:flex-end; padding:10px 8px 0;
    min-height:78px; background:linear-gradient(180deg,var(--wood3),var(--wood4));
    border-radius:4px 4px 0 0; box-shadow:inset 0 10px 16px rgba(0,0,0,.35); overflow:visible;}
  .bookface{width:34px; height:54px; border-radius:2px 4px 4px 2px; overflow:hidden; flex:none;
    transform:perspective(140px) rotateY(-7deg);
    box-shadow:5px 5px 9px rgba(0,0,0,.4), inset -4px 0 5px rgba(0,0,0,.3);
    background:linear-gradient(160deg,#dcc9ae,#ac8b66); display:flex; align-items:center;
    justify-content:center; font-size:14px; font-weight:700; color:#fff; font-family:var(--serif);}
  .bookface img{width:100%; height:100%; object-fit:cover; display:block;}
  .board-top{height:13px; margin:0 -3px; background:linear-gradient(var(--wood1),var(--wood2));
    transform:perspective(70px) rotateX(42deg); transform-origin:top;}
  .board-front{height:9px; margin:-2px -3px 12px; background:linear-gradient(var(--wood5),var(--wood6));
    border-radius:0 0 4px 4px; box-shadow:0 6px 9px rgba(0,0,0,.4);}
  .decor-free{position:absolute; font-size:26px; cursor:grab; user-select:none; z-index:4;
    touch-action:none; filter:drop-shadow(0 4px 4px rgba(0,0,0,.35)); line-height:1;}
  .decor-free:active{cursor:grabbing; transform:scale(1.15);}
  .shelf-arrow{position:absolute; right:3px; bottom:14px; z-index:5; border:none; width:22px; height:22px;
    border-radius:50%; background:rgba(0,0,0,.42); color:#fff; cursor:pointer; font-size:12px; line-height:1;}
  .shelf-arrow:hover{background:rgba(0,0,0,.6);}
  .booklink{color:var(--accent-dark); text-decoration:underline;}
  .shelf-hint{font-size:10px; color:var(--muted); text-align:center; margin-top:4px; font-style:italic;}
  /* multi-select */
  .selc{width:22px; height:22px; border-radius:50%; border:2.5px solid var(--line); flex:none;
    align-self:center; transition:all .12s;}
  .selc.on{background:var(--accent); border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft);}
  .card.selected{outline:2.5px solid var(--focus);}
  .bulkbar{position:fixed; left:0; right:0; bottom:calc(62px + env(safe-area-inset-bottom)); z-index:25;
    background:var(--card); border-top:1px solid var(--line); box-shadow:0 -4px 14px rgba(58,40,23,.15);
    display:flex; gap:8px; padding:10px 12px; justify-content:center; align-items:center; flex-wrap:wrap;}
  .bulkbar .cnt{font-size:13px; font-weight:700; color:var(--accent-dark);}

  /* ---------- pagination ---------- */
  .pager{display:flex; gap:10px; justify-content:center; align-items:center; margin:14px 0 4px;}
  .pager button{border:1px solid var(--line); background:var(--card); color:var(--accent-dark);
    border-radius:10px; padding:8px 14px; font-size:13px; font-weight:700; cursor:pointer; font-family:var(--sans);}
  .pager button:disabled{opacity:.4;}
  .pager .pg-info{font-size:12.5px; color:var(--muted); font-weight:600;}

  /* quiet captions: the cup note and shelf controls fade away unless hovered,
     leaving just the objects — the shelf keeps its small title */
  .side-coffee .coffee-note{opacity:0; transition:opacity .4s ease .8s;}
  .side-coffee:hover .coffee-note{opacity:1; transition-delay:0s; transition-duration:.15s;}
  .side-shelf .coffee-note, .side-shelf .shelf-hint{opacity:0; transition:opacity .4s ease .8s;}
  .side-shelf:hover .coffee-note, .side-shelf:hover .shelf-hint{opacity:1; transition-delay:0s; transition-duration:.15s;}


  /* captions float over user-chosen wallpapers — give them card chips so they
     read on anything, and keep the hover-fade behavior */
  .coffee-note, .shelf-title, .shelf-hint{background:var(--card);
    /* opaque fallback first: where color-mix is unsupported these captions would otherwise get
       NO chip at all, which is precisely the illegibility this rule exists to prevent */
    background:color-mix(in srgb, var(--card) 88%, transparent);
    border:1px solid var(--line); border-radius:10px; padding:4px 10px; width:max-content;
    max-width:96%; margin-left:auto; margin-right:auto; color:var(--muted);}
  .shelf-title{font-weight:700; color:var(--muted);}

  /* The empty-shelf caption was the one caption left out of the chip treatment, and it is the
     one that sits ON the shelf rather than beside it: --muted measured 1.02-2.17:1 against the
     wood tones, i.e. invisible. The 2D row's gradient is hardcoded, so that variant failed in
     BOTH themes and for every user. Chip it too; --muted is derived against --card, so the
     contrast becomes guaranteed rather than incidental. */
  .shelf-empty{background:var(--card);
    background:color-mix(in srgb, var(--card) 88%, transparent);
    border:1px solid var(--line); border-radius:10px; width:max-content; max-width:96%;
    /* margin, not padding: the 2D empty row relied on the old 20px padding for its height */
    margin:14px auto; padding:4px 10px;}
  .shelf-empty.s3d{margin:0 auto;}
