/* ============================================================================
   NALGENE · 定制瓶搭配预览
   ----------------------------------------------------------------------------
   Design rules for this build, in priority order:

   1. FIDELITY. The product is a photograph of a real cup. Every synthetic layer
      that used to sit on top of it - colour aura, edge glow, specular sweep,
      seam occlusion, idle drift - is gone. What you see is the source art, on a
      neutral backdrop, tilted only when the customer asks for it.
   2. NOTHING COVERS THE PRODUCT. The chrome is real layout (flex bands), not
      floating overlays, so no cup size or aspect can ever slide under a panel.
   3. The stage is sized from the catalogue's `fit` box, so every cup type and
      every admin upload lands centred on the same axis and the same floor.
   ========================================================================= */

/* ------------------------------------------------------------------ tokens */
:root{
  --ink:      #07080B;
  --ink-1:    #0B0D12;
  --ink-2:    #101319;
  --ink-3:    #171B23;
  --ink-4:    #1E232C;
  --line:     rgba(255,255,255,.075);
  --line-2:   rgba(255,255,255,.14);
  --bone:     #EFEBE3;
  --bone-2:   #C9C5BC;
  --mute:     #8B909C;
  --mute-2:   #5C626D;
  /* NALGENE brand blue is #007EC4; #1B9BE0 is the same hue lifted for
     legibility of small text on a near-black UI. */
  --nalgene:    #007EC4;
  --accent:     #1B9BE0;
  --accent-2:   #8FD6F5;
  --accent-dim: rgba(27,155,224,.16);
  --danger:   #D4664F;

  --f-display:"Cormorant Garamond","Noto Serif SC","Songti SC","STSong",serif;
  --f-ui:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",
         "Hiragino Sans GB","Microsoft YaHei",sans-serif;

  --ez:      cubic-bezier(.22,1,.36,1);
  --ez-soft: cubic-bezier(.4,0,.2,1);

  --topbar: 50px;
  --railh:  240px;
  --rigw:   300px;
  --stagew: 399;
  --stageh: 663;

  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 20px;
}

/* ------------------------------------------------------------------- reset */
*,*::before,*::after{ box-sizing:border-box; }
/* Author `display` rules outrank the UA's [hidden]{display:none}; without this,
   anything toggled via the hidden attribute can silently stay on screen. */
[hidden]{ display:none !important; }
html,body{ height:100%; }
html{ -webkit-text-size-adjust:100%; }
body{
  margin:0; background:var(--ink); color:var(--bone);
  font-family:var(--f-ui); font-size:14px; line-height:1.5;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  overscroll-behavior:none; -webkit-tap-highlight-color:transparent;
}
button,input,textarea,select{ font:inherit; color:inherit; }
button{ background:none; border:0; padding:0; cursor:pointer; }
h1,h2,h3,p,dl,dd,dt,figure{ margin:0; }
img{ display:block; max-width:100%; }
input[type=range]{ -webkit-appearance:none; appearance:none; background:none; width:100%; }
::selection{ background:var(--accent); color:#0A0B0E; }

/* ------------------------------------------------------------------- shell */
.app{
  position:relative; display:flex; flex-direction:column;
  height:100vh; height:100dvh; overflow:hidden;
}
.app.is-focus .topbar,
.app.is-focus .rail{ opacity:0; pointer-events:none; }
.app.is-focus .studio{ padding-bottom:6px; }
.topbar,.rail{ transition:opacity .42s var(--ez); }

/* ------------------------------------------------------------------ topbar */
.topbar{
  position:relative; z-index:30; flex:0 0 auto;
  display:flex; align-items:center; justify-content:space-between;
  padding:0 16px; height:calc(var(--topbar) + env(safe-area-inset-top));
  padding-top:env(safe-area-inset-top);
  border-bottom:1px solid var(--line);
  background:linear-gradient(to bottom,rgba(11,13,18,.94),rgba(9,10,14,.78));
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
}
.brand{ display:flex; align-items:center; gap:10px; min-width:0; }
/* the official wordmark carries its own blue badge, so it needs no colour
   treatment and reads on any background */
.brand__logo{ height:25px; width:auto; flex:0 0 auto; display:block; }
.brand__tag{
  font-size:10px; letter-spacing:.14em; color:var(--mute-2);
  padding-left:10px; margin-left:2px; border-left:1px solid var(--line-2);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* The clear button's label is six characters, so on the narrowest phones the
   tagline is the first thing to go rather than letting the bar clip. */
@media (max-width:379px){ .brand__tag{ display:none; } }
.topbar__actions{ display:flex; gap:7px; flex:0 0 auto; }
.ghost{
  display:inline-flex; align-items:center; gap:6px;
  height:30px; padding:0 11px; border-radius:999px;
  border:1px solid var(--line-2); color:var(--bone-2);
  font-size:11.5px; letter-spacing:.06em; white-space:nowrap;
  background:rgba(255,255,255,.028);
  transition:background .22s var(--ez), color .22s var(--ez), border-color .22s var(--ez);
}
.ghost:hover{ background:rgba(255,255,255,.07); color:var(--bone); }
.ghost:active{ transform:scale(.97); }
.ghost .light{
  width:5px; height:5px; border-radius:50%; background:var(--mute-2);
  transition:background .25s, box-shadow .25s;
}
.ghost[aria-pressed=true] .light{ background:var(--accent); box-shadow:0 0 7px 1px rgba(27,155,224,.65); }
.ghost[aria-pressed=true]{ border-color:rgba(27,155,224,.4); color:var(--accent-2); }
.ghost svg{ width:14px; height:14px; flex:0 0 auto; }
/* Nothing to clear yet: grey it out rather than letting the tap do nothing. */
.ghost:disabled{ opacity:.35; cursor:default; }
.ghost:disabled:hover{ background:rgba(255,255,255,.028); color:var(--bone-2); }
.ghost:disabled:active{ transform:none; }

/* ================================================================== STUDIO */
/* Neutral studio. No coloured wash: a tinted backdrop shifts how the plastic
   colour reads, which is the opposite of what a fidelity tool needs. */
.studio{
  position:relative; flex:1 1 auto; min-height:0; z-index:1;
  display:flex; flex-direction:column;
  padding:6px 10px calc(var(--railh) + 6px);
  background:var(--ink);
}
.studio__sky,
.studio__floor,
.studio__grain,
.studio__vignette{ position:absolute; inset:0; pointer-events:none; }

.studio__sky{
  background:
    radial-gradient(122% 60% at 50% 22%, rgba(196,203,216,.105) 0%, rgba(120,128,146,.045) 38%, transparent 70%),
    linear-gradient(178deg,#101217 0%,#0A0C10 46%,#05060A 100%);
}
.studio__floor{
  top:auto; bottom:0; height:38%; left:-12%; right:-12%;
  background:
    radial-gradient(56% 90% at 50% 0%, rgba(200,208,226,.075) 0%, transparent 68%),
    linear-gradient(to bottom, rgba(255,255,255,.010), rgba(0,0,0,.40));
  -webkit-mask-image:linear-gradient(to bottom, transparent, #000 58%);
          mask-image:linear-gradient(to bottom, transparent, #000 58%);
}
.studio__floor::before{
  content:''; position:absolute; left:14%; right:14%; top:0; height:1px;
  background:linear-gradient(to right, transparent, rgba(255,255,255,.13), transparent);
}
.studio__grain{
  opacity:.038; mix-blend-mode:overlay; background-size:150px 150px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23n)'/%3E%3C/svg%3E");
}
.studio__vignette{
  background:radial-gradient(126% 84% at 50% 44%, transparent 46%, rgba(0,0,0,.34) 84%, rgba(0,0,0,.62) 100%);
}

/* ------------------------------------------------------------------- hud -- */
.hud{
  position:relative; z-index:3; flex:0 0 auto;
  display:flex; align-items:center; gap:8px;
  height:28px; margin-bottom:6px;
}
.lock{
  display:inline-flex; align-items:center; gap:5px; flex:0 0 auto;
  padding:3px 9px 3px 7px; border-radius:999px;
  font-size:10.5px; letter-spacing:.05em; white-space:nowrap;
  border:1px solid rgba(27,155,224,.34); background:rgba(27,155,224,.10);
  color:var(--accent-2);
  transition:background .3s var(--ez), border-color .3s var(--ez);
}
.lock svg{ width:11px; height:11px; flex:0 0 auto; }
.lock b{ font-weight:600; }
.lock.is-flash{ animation:lockflash 1.4s var(--ez); }
@keyframes lockflash{
  0%{ background:rgba(27,155,224,.46); border-color:var(--accent); }
  100%{ background:rgba(27,155,224,.10); border-color:rgba(27,155,224,.34); }
}
.hud__spec{
  flex:1 1 auto; min-width:0; font-size:11px; color:var(--mute); letter-spacing:.03em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.hud__focus{
  flex:0 0 auto; height:26px; padding:0 12px; border-radius:999px;
  font-size:11px; letter-spacing:.05em; color:var(--bone-2);
  border:1px solid var(--line-2); background:rgba(255,255,255,.03);
  transition:background .2s, color .2s;
}
.hud__focus:hover{ background:rgba(255,255,255,.08); color:var(--bone); }

/* ------------------------------------------------------------- stage area --
   A plain box. app.js measures it and fits the catalogue's content box inside,
   which is what makes every cup land centred and fully visible.              */
.stagearea{
  position:relative; z-index:2; flex:1 1 auto; min-height:0;
}
.rigwrap{
  position:absolute;            /* left/top/width/height come from app.js */
}
/* A still. No perspective, no rotation, no depth: the source is a single
   photograph per colour, so any angle is invented, and an invented angle on a
   fidelity tool is a wrong answer. The brand's own customiser is flat too. */
.rig{ position:absolute; inset:0; }
.layer{ position:absolute; inset:0; width:100%; height:100%; pointer-events:none; }
.layer--ground{ z-index:1; }
.layer--body  { z-index:2; }
/* Every decal is clipped to the bottle's own silhouette. Artwork that hangs
   past the plastic is the single most obvious "this is a mockup" tell, and
   masking by the body image costs nothing at runtime. */
.layer--decals{
  z-index:3;
  -webkit-mask-image:var(--bodymask); mask-image:var(--bodymask);
  -webkit-mask-size:100% 100%; mask-size:100% 100%;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
}
.layer--lid   { z-index:4; }

/* ------------------------------------------------------------------ cta -- */
/* Save row inside the action panel. A hairline separates it from the
   operations above only when those are actually showing. */
.ctarow{ display:flex; justify-content:center; }
.dock__ops:not([hidden]) + .ctarow{ padding-top:11px; border-top:1px solid var(--line); }
.savebtn{
  position:relative; overflow:hidden;
  height:42px; padding:0 28px; border-radius:999px;
  display:inline-flex; align-items:center; justify-content:center; gap:9px;
  border:1px solid rgba(93,190,240,.40);
  background:linear-gradient(180deg, rgba(93,190,240,.11), rgba(93,190,240,.028));
  color:#CFEAF9; font-size:13px; font-weight:500; letter-spacing:.06em;
  -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.09);
  transition:border-color .25s var(--ez), color .25s var(--ez),
             background .25s var(--ez), transform .16s var(--ez);
}
.savebtn svg{ width:15px; height:15px; opacity:.9; }
/* a slow sheen that only crosses when hovered - craft, not glitter */
.savebtn::after{
  content:''; position:absolute; top:0; bottom:0; left:-40%; width:36%;
  background:linear-gradient(100deg, transparent, rgba(255,255,255,.16), transparent);
  transform:translateX(-120%); transition:transform .9s var(--ez); pointer-events:none;
}
.savebtn:hover{ border-color:rgba(140,214,246,.72); color:#FFFFFF;
  background:linear-gradient(180deg, rgba(93,190,240,.20), rgba(93,190,240,.07)); }
.savebtn:hover::after{ transform:translateX(420%); }
.savebtn:active{ transform:scale(.975); }
@media (max-width:400px){ .savebtn{ padding:0 20px; font-size:12.5px; } }

/* ------------------------------------------------------------------ decals */
.decal{
  position:absolute;
  left:calc(var(--x) * var(--rigw));
  top:calc(var(--y) * var(--rigw));
  width:calc(var(--s) * var(--rigw));
  height:calc(var(--s) * var(--rigw) * var(--ar,1));
  transform:translate(-50%,-50%) rotate(calc(var(--rot,0) * 1deg)) scaleX(var(--fx,1));
  transform-origin:50% 50%;
  touch-action:none; cursor:grab; pointer-events:auto;
  z-index:var(--zi,1);
}
.decal.is-text{ width:auto; height:auto; }
.decal:active{ cursor:grabbing; }
.decal__art{ position:relative; width:100%; height:100%; display:block; }
.decal__art > svg,
.decal__art > img{ width:100%; height:100%; display:block; pointer-events:none; }
.decal.is-text .decal__art > span{
  display:block; white-space:pre; line-height:1;
  font-size:calc(var(--fs) * var(--rigw));
  font-family:var(--ff,var(--f-ui));
  font-weight:var(--fw,400);
  color:var(--k,var(--bone));
  letter-spacing:calc(var(--tr,0) * .01em);
  font-synthesis:weight;
}
.decal.is-text .decal__art > span > i{ display:inline-block; font-style:normal;
  transform:skewX(calc(var(--it,0) * -1deg)); }
/* 竖排：CJK labels on a bottle are usually stacked, and upright orientation
   keeps Latin letters and digits standing up instead of lying on their side. */
.decal.is-text.is-vtext .decal__art > span{
  writing-mode:vertical-rl;
  text-orientation:upright;
  letter-spacing:calc(var(--tr,0) * .01em);
}
.decal.is-text.is-vtext .decal__art > span > i{ transform:skewY(calc(var(--it,0) * -1deg)); }
.decal__frame{
  position:absolute; inset:-5px; border:1px solid rgba(27,155,224,.78);
  border-radius:3px; opacity:0; transition:opacity .16s var(--ez);
  box-shadow:0 0 0 1px rgba(0,0,0,.35), 0 6px 24px -8px rgba(0,0,0,.8);
  pointer-events:none;
}
.decal.is-sel .decal__frame{ opacity:1; }
.decal__ui{ position:absolute; inset:-5px; opacity:0; pointer-events:none; }
.decal.is-sel .decal__ui{ opacity:1; }
.hdl{
  position:absolute; width:30px; height:30px; margin:-15px; pointer-events:auto;
  touch-action:none; cursor:pointer;
}
.hdl::after{
  content:''; position:absolute; inset:10px; border-radius:50%; background:#FBFAF7;
  box-shadow:0 0 0 1.5px rgba(10,11,14,.7), 0 2px 10px rgba(0,0,0,.6);
}
.hdl[data-h=tl]{ left:0;    top:0; }
.hdl[data-h=tr]{ left:100%; top:0; }
.hdl[data-h=bl]{ left:0;    top:100%; }
.hdl[data-h=br]{ left:100%; top:100%; }
.hdl[data-h=rot]{ left:50%; top:0; }
.hdl[data-h=rot]::after{ inset:11px; background:var(--accent); border-radius:2px; }
.decal__stem{
  position:absolute; left:50%; top:-30px; width:1px; height:30px;
  background:linear-gradient(to top, rgba(27,155,224,.9), rgba(27,155,224,.25));
}
.decal.is-off .decal__frame{ border-color:rgba(212,102,79,.9); }
.decal.is-off .hdl::after{ background:#F0C3B8; }
.decal.is-drag .decal__frame{ opacity:1; }

/* ==================================================================== RAIL */
.rail{
  position:absolute; left:0; right:0; bottom:0; z-index:20;
  display:flex; flex-direction:column; justify-content:flex-end;
}

/* ------------------------------------------------------------------- sheet */
.sheet{
  display:flex; flex-direction:column; min-height:0;
  max-height:46dvh;
  background:linear-gradient(to bottom,rgba(15,18,24,.96),rgba(9,11,15,.99));
  backdrop-filter:blur(22px); -webkit-backdrop-filter:blur(22px);
  border-top:1px solid var(--line-2);
  border-radius:var(--r-lg) var(--r-lg) 0 0;
  box-shadow:0 -22px 60px -30px rgba(0,0,0,1);
  /* NO max-height transition. The studio's bottom padding is derived from this
     element's measured height, so animating that height makes the padding a
     moving target: for the whole animation the sheet is taller than the space
     reserved for it and sits on the save button. Snapping is the honest
     behaviour - the fade carries what little motion is needed. */
  transition:opacity .22s var(--ez);
  overflow:hidden;
}
.rail.is-collapsed .sheet{ max-height:0; opacity:0; border-top-color:transparent; }
.grip{
  flex:0 0 auto; height:20px; display:grid; place-items:center; cursor:grab;
  touch-action:none;
}
.grip span{
  width:38px; height:3.5px; border-radius:99px; background:var(--line-2);
  transition:background .2s;
}
.grip:hover span{ background:rgba(255,255,255,.26); }

.sheet__body{ min-height:0; overflow-y:auto; overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch; padding:0 14px 13px; }
.sect{ display:none; }
.sect.is-on{ display:block; animation:sectin .34s var(--ez); }
@keyframes sectin{ from{ opacity:0; transform:translateY(9px); } to{ opacity:1; transform:none; } }

.sect__head{ display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:0 0 9px; }
.sect__head h2{
  font-family:var(--f-display); font-size:16.5px; font-weight:600;
  letter-spacing:.15em; color:var(--bone);
}
.pill{
  font-size:10px; letter-spacing:.09em; color:var(--mute-2);
  border:1px solid var(--line); border-radius:999px; padding:3px 9px;
  white-space:nowrap;
}
.lockpill{
  display:inline-flex; align-items:center; gap:5px; font-size:10px; letter-spacing:.08em;
  color:var(--accent-2); border:1px solid rgba(27,155,224,.32);
  background:var(--accent-dim); border-radius:999px; padding:3px 9px; white-space:nowrap;
}
.mini{
  font-size:11.5px; letter-spacing:.05em; color:var(--bone-2);
  border:1px solid var(--line-2); border-radius:999px; padding:6px 12px;
  background:rgba(255,255,255,.03); white-space:nowrap;
  transition:background .2s, color .2s, border-color .2s;
}
.mini:hover{ background:rgba(255,255,255,.08); color:var(--bone); }
.mini--accent{ border-color:rgba(27,155,224,.45); color:var(--accent-2); background:var(--accent-dim); }
.note{ font-size:11px; color:var(--mute-2); line-height:1.65; padding-top:10px; }

/* ---------------------------------------------------------------- swatches */
/* Compact on purpose: every pixel the sheet steals is a pixel off the product. */
.swatches{ display:grid; grid-template-columns:repeat(auto-fill,minmax(50px,1fr)); gap:10px 6px; }
.sw{
  display:flex; flex-direction:column; align-items:center; gap:5px;
  padding:2px 0; border-radius:var(--r-sm); position:relative;
  transition:transform .18s var(--ez);
}
.sw:active{ transform:scale(.94); }
.sw__dot{
  width:34px; height:34px; border-radius:50%; position:relative;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.12),
             inset 0 -4px 8px -4px rgba(0,0,0,.5),
             inset 0 4px 7px -4px rgba(255,255,255,.3),
             0 4px 12px -7px rgba(0,0,0,.9);
  transition:box-shadow .22s var(--ez), transform .22s var(--ez);
}
.sw__dot::after{
  content:''; position:absolute; inset:-4px; border-radius:50%;
  border:1.5px solid transparent; transition:border-color .22s var(--ez);
}
.sw.is-on .sw__dot{ transform:scale(1.09); }
.sw.is-on .sw__dot::after{ border-color:var(--accent); }
.sw.is-on .sw__name{ color:var(--bone); }
.sw__name{
  font-size:9.5px; letter-spacing:.02em; color:var(--mute); text-align:center;
  max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  transition:color .2s;
}
.sw__dot--clear{
  background:
    linear-gradient(140deg,rgba(255,255,255,.5),rgba(255,255,255,.06) 42%,rgba(255,255,255,.34)),
    #C9CFD8 !important;
}
.sw__dot--clear::before{
  content:''; position:absolute; inset:5px; border-radius:50%;
  border:1px dashed rgba(10,12,16,.35);
}

/* -------------------------------------------------------------- typecards */
.typelist{ display:grid; gap:9px; }
.tcard{
  display:grid; grid-template-columns:42px 1fr auto; align-items:center; gap:12px;
  padding:11px 13px; border-radius:var(--r-md); text-align:left;
  border:1px solid var(--line); background:rgba(255,255,255,.022);
  transition:border-color .24s var(--ez), background .24s var(--ez), transform .16s;
}
.tcard:active{ transform:scale(.985); }
.tcard.is-on{ border-color:rgba(27,155,224,.5); background:rgba(27,155,224,.07); }
.tcard__ico{
  width:42px; height:42px; border-radius:11px; display:grid; place-items:center;
  border:1px solid var(--line); background:rgba(255,255,255,.03);
}
.tcard__ico svg{ width:15px; height:26px; }
.tcard__ico rect,.tcard__ico path{ fill:none; stroke:var(--mute); stroke-width:1.4; }
.tcard.is-on .tcard__ico rect,.tcard.is-on .tcard__ico path{ stroke:var(--accent); }
.tcard__name{ font-size:13.5px; color:var(--bone); letter-spacing:.03em; }
.tcard__desc{ font-size:10.5px; color:var(--mute-2); margin-top:2px; letter-spacing:.02em; }
.tcard__spec{
  font-size:10px; letter-spacing:.1em; color:var(--mute); text-align:right;
  font-variant-numeric:tabular-nums;
}
.tcard.is-on .tcard__spec{ color:var(--accent); }

/* ----------------------------------------------------------------- sticker */
.cats{ display:flex; gap:7px; overflow-x:auto; padding-bottom:11px; scrollbar-width:none; }
.cats::-webkit-scrollbar{ display:none; }
.cat{
  flex:0 0 auto; font-size:11.5px; letter-spacing:.05em; color:var(--mute);
  border:1px solid var(--line); border-radius:999px; padding:5px 12px;
  transition:all .2s var(--ez);
}
.cat.is-on{ color:#FFFFFF; background:var(--accent); border-color:var(--accent); font-weight:600; }
.stickerbox{ display:grid; grid-template-columns:repeat(auto-fill,minmax(60px,1fr)); gap:9px; }
.stk{
  aspect-ratio:1; border-radius:var(--r-sm); display:grid; place-items:center;
  border:1px solid var(--line); background:
    radial-gradient(80% 80% at 50% 22%, rgba(255,255,255,.045), transparent 70%),
    rgba(255,255,255,.018);
  padding:12px; position:relative; transition:border-color .2s, transform .16s, background .2s;
}
.stk:active{ transform:scale(.94); }
.stk:hover{ border-color:var(--line-2); background:rgba(255,255,255,.05); }
.stk svg,.stk img{ width:100%; height:100%; color:var(--bone); pointer-events:none; }
.stk--add{ border-style:dashed; }
.stk--add span{ font-size:10px; color:var(--mute); letter-spacing:.04em; }
.stk__badge{
  position:absolute; left:4px; bottom:4px; font-size:8px; color:var(--accent);
  letter-spacing:.06em;
}

/* ------------------------------------------------------------------ editor */
.editor{ display:grid; gap:15px; }
.field{ display:grid; gap:8px; }
.field--split{ grid-template-columns:1fr 1fr; gap:14px; }
.field__top{ display:flex; align-items:center; justify-content:space-between; gap:8px; }
.field label{ font-size:10px; letter-spacing:.16em; color:var(--mute-2); text-transform:uppercase; }
.val,.count{ font-size:11px; color:var(--bone-2); font-variant-numeric:tabular-nums; }
.lic{
  font-size:9.5px; letter-spacing:.06em; color:var(--accent);
  border:1px solid rgba(27,155,224,.3); background:var(--accent-dim);
  border-radius:999px; padding:2px 8px;
}
textarea{
  width:100%; resize:none; border-radius:var(--r-sm); padding:10px 12px;
  border:1px solid var(--line-2); background:rgba(255,255,255,.03); color:var(--bone);
  font-size:14px; line-height:1.5; outline:none;
  transition:border-color .2s, background .2s;
}
textarea:focus{ border-color:rgba(27,155,224,.55); background:rgba(255,255,255,.05); }
textarea::placeholder{ color:var(--mute-2); }

/* Horizontal chips: eleven stacked rows would eat the whole product area, and
   scrolling a font rack sideways is how you actually compare typefaces. */
.fontlist{ display:flex; gap:8px; overflow-x:auto; padding:2px 0 6px; scrollbar-width:none; }
.fontlist::-webkit-scrollbar{ display:none; }
.frow{
  flex:0 0 auto; display:flex; flex-direction:column; align-items:flex-start; gap:3px;
  min-width:126px; padding:9px 13px 8px; border-radius:var(--r-sm); text-align:left;
  border:1px solid var(--line); background:rgba(255,255,255,.022);
  transition:border-color .2s, background .2s;
}
.frow.is-on{ border-color:rgba(27,155,224,.5); background:rgba(27,155,224,.08); }
.frow__demo{ font-size:16px; color:var(--bone); line-height:1.3; white-space:nowrap; }
.frow__meta{ font-size:9px; color:var(--mute-2); letter-spacing:.04em; white-space:nowrap; }
.frow.is-on .frow__meta{ color:var(--accent); }
.frow__ok{ color:var(--accent); }

input[type=range]{ height:22px; border-radius:99px; cursor:pointer; }
input[type=range]::-webkit-slider-runnable-track{ height:2px; border-radius:99px; background:var(--line-2); }
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none; width:16px; height:16px; margin-top:-7px; border-radius:50%;
  background:var(--bone); box-shadow:0 2px 10px rgba(0,0,0,.6);
  transition:transform .15s;
}
input[type=range]:active::-webkit-slider-thumb{ transform:scale(1.16); }
input[type=range]::-moz-range-track{ height:2px; border-radius:99px; background:var(--line-2); }
input[type=range]::-moz-range-thumb{ width:16px; height:16px; border:0; border-radius:50%; background:var(--bone); }

.seg{ display:flex; gap:6px; }
.seg button{
  flex:1; height:32px; border-radius:var(--r-sm); font-size:12px; color:var(--mute);
  border:1px solid var(--line); background:rgba(255,255,255,.022); transition:all .2s var(--ez);
}
.seg button.is-on{ color:#FFFFFF; background:var(--accent); border-color:var(--accent); font-weight:600; }

.inkrow{ display:flex; gap:6px; flex-wrap:wrap; }
.inkrow--lg{ gap:9px; }
/* 24px + 6px gap fits all ten inks on one row inside the dock; at 26/7 the
   tenth wrapped and the panel grew a line for nothing. */
.ink{
  width:24px; height:24px; border-radius:50%; position:relative;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16), 0 3px 9px -5px rgba(0,0,0,.9);
  transition:transform .18s var(--ez);
}
.inkrow--lg .ink{ width:32px; height:32px; }
.ink::after{
  content:''; position:absolute; inset:-4px; border-radius:50%;
  border:1.5px solid transparent; transition:border-color .2s;
}
.ink.is-on::after{ border-color:var(--accent); }
.ink.is-on{ transform:scale(1.06); }
.lockwrap{ padding:16px 0 4px; }
.lockwrap p{ font-size:11.5px; color:var(--mute-2); line-height:1.7; }

/* ===================================================================== NAV */
.nav{
  flex:0 0 auto; display:grid; grid-template-columns:repeat(5,1fr);
  padding-bottom:env(safe-area-inset-bottom);
  border-top:1px solid var(--line-2);
  background:linear-gradient(to bottom,rgba(13,16,21,.94),rgba(7,8,12,.99));
  backdrop-filter:blur(20px); -webkit-backdrop-filter:blur(20px);
}
.navbtn{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:3px; padding:9px 0 10px; color:var(--mute-2); position:relative;
  transition:color .2s var(--ez);
}
.navbtn i{ font-style:normal; font-size:14px; line-height:1; }
.navbtn span{ font-size:10px; letter-spacing:.1em; }
.navbtn.is-on{ color:var(--accent-2); }
.navbtn.is-on::before{
  content:''; position:absolute; top:0; left:50%; transform:translateX(-50%);
  width:26px; height:1.5px; background:var(--accent); border-radius:99px;
}

/* ==================================================================== DOCK */
.dock{
  /* The single bottom action panel of the product column: colour + operations
     when a decal is selected, always the save action. Previously the save button
     was its own band wedged between the artwork and the editor, so the bottom of
     the screen read as four stacked strips. */
  position:relative; flex:0 0 auto; z-index:4; margin:7px 0 0;
  display:grid; gap:9px; padding:10px;
  border-radius:var(--r-md);
  border:1px solid var(--line-2); background:rgba(12,15,20,.72);
  -webkit-backdrop-filter:blur(20px); backdrop-filter:blur(20px);
}
.dock[hidden]{ display:none; }
.dock__ink{ display:flex; align-items:center; gap:9px; }
.dock__label{ font-size:9.5px; letter-spacing:.14em; color:var(--mute-2); flex:0 0 auto; }
.dock__ops{ display:grid; grid-template-columns:repeat(6,1fr); gap:6px; }
/* Icon above label. The old row was bare text, and "上移 / 下移" read as nudging
   the sticker rather than reordering the stack - especially sitting next to
   居中. The glyph makes the operation unmistakable at a glance. */
.dock__ops button{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
  height:46px; border-radius:var(--r-sm); font-size:9.5px; letter-spacing:.02em;
  color:var(--bone-2);
  border:1px solid var(--line); background:rgba(255,255,255,.03);
  transition:background .2s, color .2s, border-color .2s, opacity .2s;
}
.dock__ops button svg{ width:16px; height:16px; display:block; flex:0 0 auto; }
.dock__ops button:hover:not(:disabled){ background:rgba(255,255,255,.09); color:var(--bone); }
.dock__ops button:active:not(:disabled){ transform:scale(.95); }
.dock__ops button.danger{ color:#E59C89; border-color:rgba(212,102,79,.3); }
/* At the top or bottom of the stack the button greys out, so the boundary is
   visible rather than only being explained by a toast after the fact. */
.dock__ops button:disabled{ opacity:.34; cursor:default; }
.dock__ops button:disabled svg{ opacity:.75; }

/* =================================================================== TOAST */
.toast{
  position:fixed; left:50%; bottom:max(calc(var(--railh) + 24px), 82px); z-index:60;
  transform:translate(-50%,14px); opacity:0; pointer-events:none;
  max-width:min(90vw,420px); padding:10px 16px; border-radius:999px;
  font-size:12px; letter-spacing:.02em; line-height:1.5; text-align:center;
  color:var(--bone); background:rgba(22,26,34,.96);
  border:1px solid var(--line-2);
  box-shadow:0 20px 44px -22px rgba(0,0,0,1);
  backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
  transition:opacity .3s var(--ez), transform .42s var(--ez), bottom .5s var(--ez);
}
.toast.is-on{ opacity:1; transform:translate(-50%,0); }

/* ================================================================== SPLASH */
.splash{
  position:fixed; inset:0; z-index:90; display:grid; place-content:center; gap:20px;
  background:var(--ink); transition:opacity .6s var(--ez), visibility .6s;
}
.splash.is-off{ opacity:0; visibility:hidden; }
.splash__logo{ height:34px; width:auto; margin:0 auto;
  animation:splashin 1.1s var(--ez) both; }
@keyframes splashin{ from{ opacity:0; transform:scale(.94); } to{ opacity:1; transform:none; } }
.splash__bar{ width:120px; height:1px; background:var(--line-2); overflow:hidden; margin:0 auto; }
.splash__bar i{ display:block; height:100%; width:36%; background:var(--accent);
  animation:splashbar 1.15s var(--ez-soft) infinite; }
@keyframes splashbar{ 0%{ transform:translateX(-110%); } 100%{ transform:translateX(320%); } }

/* ================================================================== FATAL */
.fatal{
  position:fixed; left:14px; right:14px; bottom:14px; z-index:95;
  padding:13px 16px; border-radius:12px; max-height:44dvh; overflow:auto;
  border:1px solid rgba(212,102,79,.55); background:rgba(48,18,14,.97);
  color:#F0C3B8; font:12px/1.65 ui-monospace,Menlo,Consolas,monospace;
  white-space:pre-wrap; word-break:break-word;
}
.fatal[hidden]{ display:none !important; }
/* ============================================================== DESKTOP */
@media (min-width:900px){
  .app{
    display:grid; grid-template-columns:1fr 396px;
    grid-template-rows:auto 1fr; height:100dvh;
  }
  .topbar{ grid-column:1 / -1; padding-left:26px; padding-right:26px; }
  .brand__logo{ height:28px; }
  .studio{ grid-column:1; grid-row:2; padding:18px 30px 24px; }
  .hud{ height:30px; }
  .hud__spec{ font-size:11.5px; }
  .ctarow{ padding-top:12px; }
  .savebtn{ height:46px; width:auto; min-width:220px; margin:0 auto; font-size:13.5px; }
  .rail{
    position:static; grid-column:2; grid-row:2;
    border-left:1px solid var(--line);
    background:linear-gradient(180deg,rgba(13,16,22,.7),rgba(8,10,14,.95));
  }
  .rail.is-collapsed .sheet{ max-height:none; opacity:1; }
  .sheet{
    max-height:none; flex:1 1 auto; border:0; border-radius:0;
    background:none; backdrop-filter:none; -webkit-backdrop-filter:none;
    box-shadow:none; order:2;
  }
  .grip{ display:none; }
  .nav{ order:1; border-top:0; border-bottom:1px solid var(--line);
        background:none; backdrop-filter:none; -webkit-backdrop-filter:none; }
  .navbtn{ padding:13px 0; }
  .navbtn i{ font-size:15px; }
  .sheet__body{ padding:18px 22px 28px; }
  .dock{ left:auto; right:26px; bottom:24px; width:352px; }
  .toast{ bottom:28px; left:calc(50% - 198px); }
}
@media (min-width:1280px){
  .app{ grid-template-columns:1fr 440px; }
}

/* ------------------------------------------------------- reduced motion */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.06s !important; }
}
@media (prefers-contrast:more){
  :root{ --line:rgba(255,255,255,.2); --line-2:rgba(255,255,255,.34); --mute:#A8AEBA; }
}
