/* ─────────────────────────────────────────────────────────────────────────
   PartForm · Keychain Maker — MakerLab-style redesign.
   Ported from admin app redesign/design_handoff_keychain_maker/reference/redesign.css.
   All rules namespaced `.km2-*`; tokens scoped to `.km2` (NOT :root) so the
   admin app's dark theme (admin.css) is never touched or clobbered.
   ───────────────────────────────────────────────────────────────────────── */

/* Space Grotesk 500/600, JetBrains Mono 500 and Inter 300/400/500 are already
   @font-face'd app-wide by admin.css (also linked on this page) — reuse those,
   don't re-declare. Only the two weights admin.css doesn't cover are added
   here, from the SAME already-vendored /static/fonts/ files (no new woff2). */
@font-face{font-family:'Space Grotesk';src:url('/static/fonts/space-grotesk-latin-400.woff2') format('woff2');font-weight:400;font-display:swap}
@font-face{font-family:'JetBrains Mono';src:url('/static/fonts/jetbrains-mono-latin-400.woff2') format('woff2');font-weight:400;font-display:swap}

*,*::before,*::after{box-sizing:border-box}

/* keyframes must stay at top level (cannot nest inside @media) — used by the
   mobile on-object popups' entrance, see the ≤760px block below */
@keyframes km2-pop-in{from{opacity:0}to{opacity:1}}

.km2{
  /* ── brief §6 light tokens ── */
  --bg:#f3efe8; --bg-2:#e9e4db; --bg-3:#fbf9f5; --bg-4:#e0d9cc;
  --fg:#18160f; --fg-dim:#5a5347; --fg-dim-2:#7a7365;
  --line:#ccc7be; --line-strong:#b8b1a3;
  --accent:#c2600f; --accent:oklch(0.58 0.18 48);
  --accent-deep:#8a4409;
  --accent-soft:color-mix(in srgb, var(--accent) 12%, transparent);
  --accent-ghost:color-mix(in srgb, var(--accent) 7%, transparent);
  --warn:#a8540f;
  --plate-ink:#161512;

  --rail-w:56px; --panel-w:364px;
  --grid:rgba(160,90,10,.22);

  position:fixed; inset:0;
  display:flex; flex-direction:column;
  font-family:'Inter',system-ui,sans-serif; font-weight:300;
  color:var(--fg); background:var(--bg);
  -webkit-font-smoothing:antialiased;
}
.km2 [hidden]{display:none !important}
.km2 button{font-family:inherit}
.km2 ::selection{background:var(--accent-soft)}

/* ───────────────────────── top bar ───────────────────────── */
.km2-top{
  position:relative;   /* the mode switch centres against this */
  flex-shrink:0; display:flex; align-items:center; justify-content:space-between;
  gap:12px; height:56px; padding:0 20px;
  background:var(--bg-2); border-bottom:1px solid var(--line);
}
.km2-brandwrap{display:flex; align-items:center; gap:14px; min-width:0}
.km2-back{
  width:30px; height:30px; display:grid; place-items:center; flex-shrink:0;
  border:1px solid var(--line-strong); background:var(--bg-3); color:var(--fg-dim);
  border-radius:7px; cursor:pointer;
}
.km2-back:hover{color:var(--accent); border-color:var(--accent)}
/* LABELLED variant of the pill above, for the two pages whose back control is
   the page's ONLY navigation and therefore has to name its destination (the
   lamp tool → the maker hub; the hub → the marketing site). `.km2-back` is a
   fixed 30×30 icon box, so text put inside it spilled out of the 56px bar in
   both locales — this one sizes to its content, keeps the same chevron and
   pill treatment, and stays on one line (ellipsising rather than pushing the
   Order button off a narrow phone). The keychain does not use it: its back
   control is icon-only next to the wordmark, and `.km2-back` is untouched. */
.km2-backlink{
  display:inline-flex; align-items:center; gap:7px; flex-shrink:1; min-width:0;
  height:30px; padding:0 11px;
  border:1px solid var(--line-strong); background:var(--bg-3); color:var(--fg-dim);
  border-radius:7px; cursor:pointer; text-decoration:none;
  font-size:12.5px; line-height:1; white-space:nowrap;
}
.km2-backlink svg{width:14px; height:14px; flex-shrink:0}
.km2-backlink span{overflow:hidden; text-overflow:ellipsis}
.km2-backlink:hover{color:var(--accent); border-color:var(--accent)}
.km2-brand{display:flex; align-items:baseline; gap:10px; min-width:0}
.km2-brand-mark{font-family:'Space Grotesk',sans-serif; font-weight:600; font-size:16px; letter-spacing:-.02em; color:var(--fg); white-space:nowrap}
.km2-brand-mark b{color:var(--accent); font-weight:600}
.km2-eyebrow{font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.18em; text-transform:uppercase; color:var(--fg-dim-2); white-space:nowrap}
/* real PartForm logo (black-ink variant, for the light top bar); links to the
   marketing site. Wordmark on desktop, compact mark on narrow screens. */
.km2-back{text-decoration:none}
.km2-brandlink{display:inline-flex; align-items:center; text-decoration:none; flex-shrink:0}
.km2-logo{display:block; width:auto}
.km2-logo-wordmark{height:22px}
.km2-logo-mark{height:26px; display:none}
@media (max-width:560px){
  .km2-logo-wordmark{display:none}
  .km2-logo-mark{display:block}
}
.km2-top-actions{display:flex; align-items:center; gap:8px}
.km2-iconbtn{
  width:34px; height:34px; display:grid; place-items:center;
  border:1px solid transparent; background:transparent; color:var(--fg-dim);
  border-radius:8px; cursor:pointer;
}
.km2-iconbtn:hover{color:var(--accent); background:var(--accent-ghost)}
.km2-iconbtn svg{width:18px; height:18px}
.km2-divider-v{width:1px; height:22px; background:var(--line); margin:0 2px}

/* ── how-it-works notice (dismissible band under the top bar) ── */
.km2-notice{
  flex-shrink:0; display:flex; align-items:center; justify-content:space-between;
  gap:16px; padding:9px 20px;
  background:var(--accent-ghost); border-bottom:1px solid var(--line); color:var(--fg);
}
.km2-notice-body{display:flex; align-items:center; gap:8px 16px; flex-wrap:wrap; min-width:0}
.km2-notice-title{font-family:'Space Grotesk',sans-serif; font-weight:600; font-size:13px; white-space:nowrap}
.km2-notice-steps{
  display:flex; align-items:center; gap:6px 16px; flex-wrap:wrap;
  margin:0; padding:0; list-style:none; counter-reset:kmstep;
  font-size:12.5px; color:var(--fg-dim);
}
.km2-notice-steps li{counter-increment:kmstep; display:flex; align-items:center; gap:7px}
.km2-notice-steps li::before{
  content:counter(kmstep); flex-shrink:0;
  width:17px; height:17px; border-radius:50%; display:inline-grid; place-items:center;
  background:var(--accent); color:#fff; font-size:9.5px; font-weight:700; font-family:'JetBrains Mono',monospace;
}
.km2-notice-steps b{color:var(--fg); font-weight:600}
.km2-notice-nopay{font-size:11.5px; font-weight:600; color:var(--accent); white-space:nowrap; text-transform:uppercase; letter-spacing:.04em}
.km2-notice-dismiss{flex-shrink:0; padding:5px 13px; font-size:12px}
@media (max-width:820px){
  .km2-notice{align-items:flex-start}
  .km2-notice-body{gap:6px 12px}
}

/* ───────────────────────── buttons ───────────────────────── */
.km2-btn{
  font-size:12.5px; font-weight:500; padding:8px 14px; border-radius:8px;
  border:1px solid var(--line-strong); background:transparent; color:var(--fg);
  cursor:pointer; display:inline-flex; align-items:center; gap:8px;
  transition:background .15s,color .15s,border-color .15s;
}
.km2-btn svg{width:15px; height:15px}
.km2-btn-ghost:hover{border-color:var(--accent); color:var(--accent)}
.km2-btn-primary{background:var(--accent); color:#fff; border-color:var(--accent); font-weight:600}
.km2-btn-primary:hover{background:var(--accent-deep); border-color:var(--accent-deep)}
.km2-btn-primary:disabled{opacity:.4; cursor:not-allowed}
.km2-btn-block{width:100%; justify-content:center}

/* ───────────────────────── app body ───────────────────────── */
.km2-body{flex:1; min-height:0; display:flex}

/* ── left tool rail ── */
.km2-rail{
  flex-shrink:0; width:var(--rail-w);
  background:var(--bg-2); border-right:1px solid var(--line);
  display:flex; flex-direction:column; align-items:center; gap:6px;
  padding:12px 0;
}
.km2-tool{
  width:40px; height:40px; display:grid; place-items:center;
  border:1px solid transparent; background:transparent; color:var(--fg-dim);
  border-radius:9px; cursor:pointer; position:relative; transition:all .13s;
}
.km2-tool svg{width:19px; height:19px}
.km2-tool:hover{color:var(--fg); background:var(--bg-4)}
.km2-tool.is-active{color:#fff; background:var(--accent); border-color:var(--accent)}
.km2-tool:disabled{opacity:.32; cursor:not-allowed}
.km2-tool:disabled:hover{background:transparent; color:var(--fg-dim)}
.km2-tool[data-tip]:hover::after{
  content:attr(data-tip); position:absolute; left:calc(100% + 10px); top:50%;
  transform:translateY(-50%); white-space:nowrap; z-index:40;
  font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.06em;
  text-transform:uppercase; color:var(--bg); background:var(--fg);
  padding:5px 8px; border-radius:6px; pointer-events:none;
}
.km2-rail-sep{width:22px; height:1px; background:var(--line); margin:4px 0}
.km2-rail-spacer{flex:1}

/* ── stage / canvas ── */
.km2-stage{
  flex:1; min-width:0; position:relative; overflow:hidden;
  background-color:var(--bg);
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size:40px 40px;
  touch-action:none;
}
/* the real Three.js canvas fills the stage — swapped in for the design's mock
   static-CSS "plate"; the engine (keychain-maker.js) renders directly into it */
.km2-scene{position:absolute; inset:0}
.km2-scene canvas{position:absolute; inset:0; width:100%; height:100%; display:block; touch-action:none}

/* overlay: size readout */
.km2-dims{
  position:absolute; top:16px; left:16px; z-index:12;
  font-family:'JetBrains Mono',monospace; font-size:13px; color:var(--fg);
  background:color-mix(in srgb, var(--bg) 84%, transparent);
  border:1px solid var(--line); border-radius:8px; padding:6px 12px;
  backdrop-filter:blur(3px);
}
.km2-dims b{color:var(--accent); font-weight:500}

/* overlay: view tag (brand motif) */
.km2-viewtag{
  position:absolute; top:16px; right:16px; z-index:12;
  font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--fg-dim-2);
  border:1px solid var(--line); border-radius:6px; padding:4px 8px;
  background:color-mix(in srgb, var(--bg) 84%, transparent);
}

/* overlay: warning */
.km2-warn{
  position:absolute; left:16px; right:16px; bottom:16px; z-index:14; max-width:520px;
  --wsoft:color-mix(in srgb, var(--warn) 13%, var(--bg));
  --wline:color-mix(in srgb, var(--warn) 42%, var(--line));
  background:var(--wsoft); border:1px solid var(--wline); color:var(--warn);
  border-radius:10px; padding:10px 34px 10px 14px; font-size:12.5px; line-height:1.45;
  display:flex; gap:9px;
}
.km2-warn svg{width:16px; height:16px; flex-shrink:0; margin-top:1px}
.km2-warn-x{position:absolute; top:6px; right:6px; width:22px; height:22px; border:none; background:transparent; color:var(--warn); opacity:.7; cursor:pointer; font-size:15px; line-height:1; border-radius:6px}
.km2-warn-x:hover{opacity:1; background:color-mix(in srgb, var(--warn) 18%, transparent)}

/* overlay: objects / group panel (bottom-left) */
.km2-objects{
  position:absolute; left:16px; bottom:16px; z-index:11; width:210px;
  background:color-mix(in srgb, var(--bg-3) 92%, transparent); backdrop-filter:blur(4px);
  border:1px solid var(--line); border-radius:11px; overflow:hidden;
  box-shadow:0 8px 24px -14px rgba(20,12,4,.4);
}
.km2-objects-head{
  display:flex; align-items:center; justify-content:space-between;
  padding:9px 12px; border-bottom:1px solid var(--line);
}
.km2-objects-head span{font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--fg-dim-2)}
.km2-objects-list{display:flex; flex-direction:column; padding:5px}
.km2-obj{
  display:flex; align-items:center; gap:9px; padding:7px 9px; border-radius:7px;
  cursor:pointer; color:var(--fg-dim); font-size:12.5px; text-align:left;
  background:transparent; border:1px solid transparent; width:100%;
}
.km2-obj:hover{background:var(--bg-4); color:var(--fg)}
.km2-obj.is-selected{background:var(--accent-soft); color:var(--fg); border-color:color-mix(in srgb,var(--accent) 35%,transparent)}
.km2-obj svg{width:15px; height:15px; flex-shrink:0; color:var(--fg-dim-2)}
.km2-obj.is-selected svg{color:var(--accent)}
.km2-obj-label{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

/* overlay: 2D/3D view toggle (bottom-right) — replaces the old decorative
   mini-preview stub (it showed stale static text, never the live design).
   No thumbnail here by design: it just swaps the active camera. */
.km2-viewtoggle{
  position:absolute; right:16px; bottom:16px; z-index:11;
  background:color-mix(in srgb, var(--bg-3) 92%, transparent); backdrop-filter:blur(4px);
  border:1px solid var(--line); border-radius:11px; padding:4px;
  box-shadow:0 8px 24px -14px rgba(20,12,4,.4);
  display:flex; gap:2px;
}
.km2-viewtoggle button{
  font-family:'JetBrains Mono',monospace; font-size:11px; font-weight:600; letter-spacing:.04em;
  color:var(--fg-dim); background:transparent; border:none; border-radius:8px;
  padding:8px 14px; cursor:pointer; transition:background .13s,color .13s;
}
.km2-viewtoggle button:hover{color:var(--fg); background:var(--bg-4)}
.km2-viewtoggle button.is-active{color:#fff; background:var(--accent)}

/* floating attachment popup (owner feedback #2) — click-to-edit, positioned
   via JS near the clicked attachment. Per brand: depth from a border/accent
   treatment, deliberately NO drop shadow (unlike the HUD overlays above). */
.km2-att-popup{
  position:absolute; z-index:30; width:228px;
  background:var(--bg-3); border:1.5px solid var(--accent); border-radius:11px;
  padding:12px; display:flex; flex-direction:column; gap:12px;
}
.km2-att-popup-head{
  display:flex; align-items:center; justify-content:space-between;
  font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--accent); font-weight:600;
}
.km2-att-popup-x{
  width:20px; height:20px; flex-shrink:0; display:grid; place-items:center;
  border:none; background:transparent; color:var(--fg-dim); cursor:pointer;
  font-size:15px; line-height:1; border-radius:5px;
}
.km2-att-popup-x:hover{color:var(--accent); background:var(--accent-ghost)}

/* floating TEXT popup (on-object quick-edit) — same positioning convention
   as .km2-att-popup above (positionTextPopup() in keychain-maker.js), under
   its own .km2-objpop hooks so claude.design's later visual pass can restyle
   it independently. Functional styling only here. */
.km2-objpop{
  position:absolute; z-index:30; width:228px;
  background:var(--bg-3); border:1.5px solid var(--accent); border-radius:11px;
  padding:12px; display:flex; flex-direction:column; gap:12px;
}
.km2-objpop-head{
  display:flex; align-items:center; justify-content:space-between;
  font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--accent); font-weight:600;
}
.km2-objpop-x{
  width:20px; height:20px; flex-shrink:0; display:grid; place-items:center;
  border:none; background:transparent; color:var(--fg-dim); cursor:pointer;
  font-size:15px; line-height:1; border-radius:5px;
}
.km2-objpop-x:hover{color:var(--accent); background:var(--accent-ghost)}
.km2-objpop-swatches{display:flex; gap:6px; flex-wrap:wrap}
.km2-objpop-swatch{
  width:24px; height:24px; border-radius:6px; border:1.5px solid var(--line-strong);
  cursor:pointer; padding:0;
}
.km2-objpop-swatch.is-active{border-color:var(--accent); box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 35%,transparent)}

/* ── lamp element editor popup — reuses .km2-objpop chrome ────── */
.km2-objpop--el{width:248px;}
/* One swatch ROW per colour the element is made of — a logo traced in its own
   colours has several. Scoped to the lamp's popup so the keychain's single-row
   swatch strip is untouched. */
.km2-elpanel .km2-objpop-swatches{flex-direction:column; align-items:flex-start; gap:6px}
.km2-objpop-swatchrow{display:flex; gap:6px; flex-wrap:wrap}
.km2-objpop--el .km2-field{gap:6px;}

.km2-title{
  flex:1; min-width:0; max-width:240px; margin-right:auto;
  font-family:'Space Grotesk',sans-serif; font-size:14px; font-weight:500; color:var(--fg);
  background:transparent; border:1px solid transparent; border-radius:7px;
  padding:5px 9px; margin-left:4px;
}
.km2-title:hover{border-color:var(--line)}
.km2-title:focus{outline:none; border-color:var(--accent); background:var(--bg-3)}
/* Below this the centred mode switch and the title compete for the same
   pixels, and the switch is the one you cannot do without. */
@media (max-width:900px){ .km2-title{display:none} }

/* Undo / redo, bottom-left of the stage — the only always-on canvas chrome. */
.km2-histbar{
  position:absolute; right:14px; bottom:14px; z-index:20; display:flex; gap:2px;
  padding:4px; border-radius:11px;
  background:color-mix(in srgb, var(--bg-3) 92%, transparent);
  border:1px solid var(--line); box-shadow:0 8px 24px -16px rgba(20,12,4,.4);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
}
.km2-histbar button{
  width:32px; height:32px; display:grid; place-items:center;
  border:none; background:transparent; color:var(--fg-dim);
  border-radius:8px; cursor:pointer;
}
.km2-histbar button svg{width:17px; height:17px}
.km2-histbar button:hover:not(:disabled){color:var(--accent); background:var(--accent-ghost)}
.km2-histbar button:disabled{opacity:.32; cursor:default}

/* PREVIEW mode darkens the stage itself — the page's warm grid is a drawing
   surface, and what Preview is for is looking at the object with nothing else
   competing. CSS only: the canvas is alpha:true and preview.png is read off the
   canvas, so none of this reaches the owner's attachment. */
.km2-stage.is-preview{
  background-color:#2a2622;
  background-image:radial-gradient(120% 90% at 50% 30%, #3a352f 0%, #211e1a 100%);
  background-size:auto;        /* the base rule tiles at 40×40 — that would band the gradient */
  background-position:0 0;
  background-repeat:no-repeat;
}
.km2-stage.is-preview .km2-histbar{
  background:color-mix(in srgb, #100e0c 62%, transparent);
  border-color:rgba(255,255,255,.12); color:#e8e2d8;
}
.km2-stage.is-preview .km2-histbar button{color:#cfc7ba}

/* ── DESIGN / PREVIEW, top centre ───────────────────────────────
   The two things this tool does — lay a design out flat, and look at the lit
   object — want opposite cameras, so they are a mode, not a view chip. */
.km2-modes{
  position:absolute; left:50%; transform:translateX(-50%);
  display:flex; gap:2px; padding:3px; flex-shrink:0;
  background:var(--bg-4); border:1px solid var(--line); border-radius:9px;
}
.km2-modes button{
  font-size:12.5px; font-weight:500; color:var(--fg-dim);
  background:transparent; border:none; border-radius:7px;
  padding:6px 18px; cursor:pointer; transition:background .13s,color .13s;
}
.km2-modes button:hover{color:var(--fg)}
.km2-modes button.is-active{color:#fff; background:var(--accent); font-weight:600}

/* ── the element editor, in the panel ───────────────────────────
   It used to be a card floating over the very thing it edits. Now selecting an
   element swaps the panel's contents: [data-lm-overall] out, this in. */
.km2-elpanel{display:flex; flex-direction:column}
.km2-backsec{
  display:flex; align-items:center; gap:6px; width:100%;
  padding:12px 18px 10px; border:none; background:transparent;
  color:var(--fg-dim); font-size:12.5px; cursor:pointer; text-align:left;
}
.km2-backsec svg{width:14px; height:14px; flex-shrink:0}
.km2-backsec:hover{color:var(--accent)}
.km2-elpanel .km2-sec-title{padding:0 18px 4px}

/* ── rotation row ───────────────────────────────────────────────
   The dial is gone from the card: rotation is an on-canvas ring around the
   element now (maker/rot-gizmo.js). What stays here is what a ring is bad at —
   an exact number, and the quarter turns. */
.km2-rotrow{display:flex; align-items:center; gap:8px}
.km2-rotnum{position:relative; flex:0 0 84px}
.km2-rotnum .km2-input{
  width:100%; padding-right:20px;
  font-family:'JetBrains Mono',monospace; font-variant-numeric:tabular-nums;
}
.km2-rotdeg{
  position:absolute; right:8px; top:50%; transform:translateY(-50%);
  font-family:'JetBrains Mono',monospace; font-size:12px;
  color:var(--fg-dim); pointer-events:none;
}
.km2-rotpresets{display:flex; gap:4px; flex:1}
.km2-rotpreset{
  flex:1; font-family:'JetBrains Mono',monospace; font-size:11px; padding:6px 4px;
  border:1.5px solid var(--line-strong); border-radius:7px;
  background:var(--bg-3); color:var(--fg-dim); cursor:pointer;
}
.km2-rotpreset:hover{border-color:var(--accent); color:var(--accent)}
.km2-rotpreset.is-active{border-color:var(--accent); background:var(--accent-ghost); color:var(--accent)}

/* The angle readout while the ring is being dragged — pinned at the element by
   lamp-maker.js's showRotHud(), NOT in the card, which is the thing the
   customer just dragged out of the way. Never interactive: it must not eat the
   drag it is reporting on. */
.km2-rothud{
  position:absolute; z-index:31; pointer-events:none;
  font-family:'JetBrains Mono',monospace; font-size:12px; font-weight:600;
  font-variant-numeric:tabular-nums; letter-spacing:-.02em;
  color:#fff; background:var(--accent); border-radius:7px; padding:4px 8px;
  box-shadow:0 8px 22px -14px rgba(20,12,4,.6);
}

/* ───────────────────────── right panel ───────────────────────── */
.km2-panel{
  flex-shrink:0; width:var(--panel-w); overflow-y:auto;
  background:var(--bg-2); border-left:1px solid var(--line);
  display:flex; flex-direction:column;
}
.km2-tabs{
  display:flex; gap:2px; padding:14px 18px 0; position:sticky; top:0; z-index:3;
  background:var(--bg-2); border-bottom:1px solid var(--line);
}
.km2-tab{
  font-size:13px; font-weight:500; color:var(--fg-dim);
  background:transparent; border:none; border-bottom:2px solid transparent;
  padding:10px 8px 12px; cursor:pointer; transition:color .15s,border-color .15s;
}
.km2-tab:hover{color:var(--fg)}
.km2-tab.is-active{color:var(--accent); border-bottom-color:var(--accent); font-weight:600}

.km2-tabpanel{display:flex; flex-direction:column}

/* section = a labelled group, hairline-divided */
.km2-sec{padding:18px; display:flex; flex-direction:column; gap:14px}
.km2-sec + .km2-sec{border-top:1px solid var(--line)}

/* ── accordion sections ─────────────────────────────────────────
   <details>/<summary>, so the open/closed state is the browser's and needs no
   JS. Collapsed, a section is just its title row — which is what turns the
   panel from a wall of controls into a short list you scan. */
details.km2-sec:not([open]){padding-bottom:14px}
summary.km2-sec-title{
  cursor:pointer; list-style:none; user-select:none;
  display:flex; align-items:center; justify-content:space-between; gap:8px;
}
summary.km2-sec-title::-webkit-details-marker{display:none}
summary.km2-sec-title::after{
  content:''; width:8px; height:8px; flex-shrink:0; margin-right:2px;
  border-right:1.6px solid currentColor; border-bottom:1.6px solid currentColor;
  transform:rotate(45deg) translate(-2px,-2px); transition:transform .15s;
}
details[open] > summary.km2-sec-title::after{transform:rotate(-135deg) translate(-2px,-2px)}
summary.km2-sec-title:hover{color:var(--fg-dim)}
summary.km2-sec-title:focus-visible{outline:2px solid var(--accent); outline-offset:3px; border-radius:3px}
.km2-sec-head{display:flex; align-items:center; justify-content:space-between; min-height:16px}
.km2-sec-title{font-family:'JetBrains Mono',monospace; font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--fg-dim-2); font-weight:600}
.km2-sec-hint{font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.04em; text-transform:none; color:var(--fg-dim-2); font-weight:400}

/* field primitives */
.km2-field{display:flex; flex-direction:column; gap:6px}
.km2-row{display:flex; gap:12px}
.km2-row > *{flex:1; min-width:0}
.km2-row.km2-row-end{align-items:flex-end}
.km2-label{
  font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--fg-dim-2);
  display:flex; align-items:center; justify-content:space-between; gap:8px;
}
.km2-readout{color:var(--accent); text-transform:none; letter-spacing:0; font-family:'JetBrains Mono',monospace; font-size:11px}

.km2-input,.km2-select{
  font-family:'Inter',sans-serif; font-size:13px; color:var(--fg);
  background:var(--bg-3); border:1px solid var(--line-strong); border-radius:8px;
  padding:8px 10px; width:100%;
}
.km2-input:focus,.km2-select:focus{outline:none; border-color:var(--accent)}
.km2-select{
  appearance:none; cursor:pointer; padding-right:30px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%237a7365' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 10px center;
}
.km2-select-sm{font-size:12px; padding:7px 26px 7px 9px}

/* range slider */
.km2-range{
  -webkit-appearance:none; appearance:none; width:100%; height:4px; margin:8px 0;
  border-radius:2px; background:var(--line-strong); accent-color:var(--accent); outline:none;
}
.km2-range::-webkit-slider-thumb{-webkit-appearance:none; appearance:none; width:16px; height:16px; border-radius:50%; background:var(--accent); border:2px solid var(--bg-2); cursor:pointer}
.km2-range::-moz-range-thumb{width:16px; height:16px; border-radius:50%; background:var(--accent); border:2px solid var(--bg-2); cursor:pointer}
.km2-range::-moz-range-track{height:4px; border-radius:2px; background:var(--line-strong)}

/* checkbox toggle */
.km2-check{display:flex; align-items:center; gap:9px; font-size:13px; color:var(--fg); cursor:pointer}
.km2-check input{accent-color:var(--accent); width:16px; height:16px; cursor:pointer}
.km2-check-lg{font-weight:500}

/* text lines */
.km2-lines{display:flex; flex-direction:column; gap:6px}
.km2-line-row{display:flex; gap:6px}
.km2-line-row .km2-input{flex:1}
.km2-line-x{
  flex-shrink:0; width:34px; border:1px solid var(--line-strong); background:transparent;
  color:var(--fg-dim); border-radius:8px; cursor:pointer; font-size:16px; line-height:1;
}
.km2-line-x:hover{color:var(--accent); border-color:var(--accent)}

/* svg upload dropzone */
.km2-drop{
  border:1.5px dashed var(--line-strong); border-radius:10px; background:var(--bg-3);
  padding:16px; text-align:center; cursor:pointer; transition:border-color .15s,background .15s;
  display:flex; flex-direction:column; align-items:center; gap:6px;
}
.km2-drop:hover,.km2-drop.is-over{border-color:var(--accent); background:var(--accent-ghost)}
.km2-drop svg{width:22px; height:22px; color:var(--fg-dim-2)}
.km2-drop-main{font-size:12.5px; color:var(--fg)}
.km2-drop-main u{color:var(--accent); text-decoration:none; font-weight:500}
.km2-drop-sub{font-family:'JetBrains Mono',monospace; font-size:9.5px; letter-spacing:.08em; text-transform:uppercase; color:var(--fg-dim-2)}
.km2-loaded{display:flex; align-items:center; gap:10px; padding:10px 12px; border:1px solid var(--line-strong); border-radius:10px; background:var(--bg-3)}
.km2-loaded svg{width:26px; height:26px; color:var(--accent); flex-shrink:0}
.km2-loaded-meta{flex:1; min-width:0; display:flex; flex-direction:column; gap:1px}
.km2-loaded-name{font-family:'JetBrains Mono',monospace; font-size:11.5px; color:var(--fg); word-break:break-all}
.km2-loaded-sub{font-size:11px; color:var(--fg-dim-2)}
.km2-loaded-x{flex-shrink:0; width:28px; height:28px; border:1px solid var(--line-strong); background:transparent; color:var(--fg-dim); border-radius:7px; cursor:pointer; font-size:15px; display:grid; place-items:center}
.km2-loaded-x:hover{color:var(--accent); border-color:var(--accent)}

/* filament colors */
.km2-colors{display:flex; gap:10px; align-items:flex-start; flex-wrap:wrap}
.km2-color-slot{position:relative; display:flex; flex-direction:column; align-items:center; gap:5px}
.km2-swatch{
  position:relative; width:46px; height:38px; border-radius:8px; cursor:pointer;
  border:1px solid var(--line-strong); overflow:hidden;
}
.km2-swatch input[type=color]{position:absolute; inset:-4px; width:calc(100% + 8px); height:calc(100% + 8px); border:none; padding:0; background:transparent; cursor:pointer}
.km2-swatch-num{position:absolute; top:3px; left:5px; font-family:'JetBrains Mono',monospace; font-size:9px; font-weight:500; color:#fff; mix-blend-mode:difference; pointer-events:none}
.km2-add-color{
  width:46px; height:38px; border:1.5px dashed var(--line-strong); border-radius:8px;
  background:var(--bg-3); color:var(--fg-dim-2); cursor:pointer; display:grid; place-items:center;
}
.km2-add-color:hover{border-color:var(--accent); color:var(--accent)}
.km2-add-color svg{width:16px; height:16px}
.km2-eyedrop{width:38px; height:38px; border:1px solid var(--line-strong); border-radius:8px; background:var(--bg-3); color:var(--fg-dim); cursor:pointer; display:grid; place-items:center}
.km2-eyedrop:hover{border-color:var(--accent); color:var(--accent)}
.km2-eyedrop svg{width:16px; height:16px}

/* a color <select> that shows the mapped swatch */
.km2-rolegrid{display:grid; grid-template-columns:1fr 1fr; gap:12px}

/* Plate: front/back + shape mode */
.km2-stub{opacity:.75} /* unused for now — kept for any future not-yet-wired control */
.km2-segwrap{display:flex; gap:8px; align-items:center}
.km2-seg{display:inline-flex; background:var(--bg-3); border:1px solid var(--line-strong); border-radius:8px; padding:2px}
.km2-seg button{
  font-size:12px; font-weight:500; color:var(--fg-dim); background:transparent; border:none;
  padding:6px 12px; border-radius:6px; cursor:pointer; display:inline-flex; align-items:center; gap:6px;
}
.km2-seg button.is-active{background:var(--accent); color:#fff}
.km2-seg button svg{width:14px; height:14px}

/* Attachments */
.km2-att-list{display:flex; flex-direction:column; gap:8px}
.km2-att-item{
  display:flex; align-items:center; gap:10px; padding:9px 10px;
  border:1px solid var(--line-strong); border-radius:9px; background:var(--bg-3);
  cursor:pointer; transition:border-color .13s,background .13s;
}
.km2-att-item:hover{border-color:var(--accent)}
.km2-att-item.is-selected{border-color:var(--accent); background:var(--accent-ghost)}
.km2-att-badge{width:26px; height:26px; flex-shrink:0; display:grid; place-items:center; border-radius:6px; background:var(--bg-4); color:var(--fg-dim)}
.km2-att-item.is-selected .km2-att-badge{background:var(--accent); color:#fff}
.km2-att-badge svg{width:15px; height:15px}
.km2-att-meta{flex:1; min-width:0; display:flex; flex-direction:column; gap:1px}
.km2-att-name{font-size:12.5px; font-weight:500; color:var(--fg)}
.km2-att-sub{font-family:'JetBrains Mono',monospace; font-size:9.5px; letter-spacing:.04em; text-transform:uppercase; color:var(--fg-dim-2)}
.km2-att-del{flex-shrink:0; width:28px; height:28px; border:1px solid transparent; background:transparent; color:var(--fg-dim-2); border-radius:7px; cursor:pointer; display:grid; place-items:center}
.km2-att-del:hover{color:var(--warn); border-color:color-mix(in srgb,var(--warn) 40%,transparent)}
.km2-att-del svg{width:15px; height:15px}
.km2-att-empty{font-size:12px; color:var(--fg-dim-2); line-height:1.5; padding:2px 0}

.km2-att-editor{
  margin-top:2px; padding:14px; border:1px solid var(--line); border-radius:11px;
  background:color-mix(in srgb,var(--bg-3) 60%,var(--bg-2));
  display:flex; flex-direction:column; gap:14px;
}
.km2-att-editor-head{display:flex; align-items:center; gap:8px; font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; text-transform:uppercase; color:var(--accent)}
.km2-att-editor-head svg{width:14px; height:14px}

/* snap-preset picker grid (3x3 like a plate map) */
.km2-snapgrid{
  display:grid; grid-template-columns:repeat(3,1fr); gap:5px;
  aspect-ratio:16/9; padding:6px; border:1px solid var(--line-strong);
  border-radius:9px; background:var(--plate-ink);
}
.km2-snapgrid button{
  border:none; border-radius:5px; background:rgba(255,255,255,.06); cursor:pointer;
  display:grid; place-items:center; transition:background .12s;
}
.km2-snapgrid button::after{content:''; width:7px; height:7px; border-radius:50%; background:rgba(255,255,255,.28)}
.km2-snapgrid button:hover{background:rgba(255,255,255,.14)}
.km2-snapgrid button.is-active{background:var(--accent)}
.km2-snapgrid button.is-active::after{background:#fff}

.km2-drag-hint{display:flex; align-items:center; gap:7px; font-size:11.5px; color:var(--fg-dim-2)}
.km2-drag-hint svg{width:14px; height:14px; color:var(--accent)}

/* nudge fallback */
.km2-nudge{display:inline-flex; gap:4px}
.km2-nudge button{width:30px; height:30px; border:1px solid var(--line-strong); background:var(--bg-3); color:var(--fg-dim); border-radius:7px; cursor:pointer; display:grid; place-items:center}
.km2-nudge button:hover{border-color:var(--accent); color:var(--accent)}
.km2-nudge svg{width:14px; height:14px}
.km2-pos-readout{font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--fg-dim); align-self:center}

/* subfields under a toggle */
.km2-subfields{display:flex; flex-direction:column; gap:14px; padding-left:14px; border-left:2px solid var(--line); margin-top:2px}

/* mobile category sheets (grouped .km2-sec sections for the bottom nav) —
   a plain flex column, invisible to layout, so wrapping existing sections in
   these on desktop changes nothing: [data-km-cat] is only ever shown/hidden
   by JS on mobile (setCategory() in keychain-maker.js). The divider hairline
   between categories now lives on the wrapper (sections that stayed true DOM
   siblings within ONE sheet still get their own divider from the
   `.km2-sec + .km2-sec` rule above). */
.km2-sheet{display:flex; flex-direction:column}
.km2-sheet + .km2-sheet{border-top:1px solid var(--line)}

/* mobile-only chrome — bottom nav + edge-pinned canvas controls + the sheet's
   grab handle/header/scroll body + the footer bar; hidden on desktop, shown
   by the @media block below. .km2-sheet-scroll is `contents` (not `none`) on
   desktop so wrapping .km2-tabs + both .km2-tabpanel in it (done purely to
   give the mobile sheet ONE scroll container — see keychain-maker.hbs)
   changes nothing about the existing desktop layout or its
   `.km2-tabs{position:sticky}` scroll-parent. */
.km2-bottomnav,.km2-canvasctl,.km2-dimtoggle,.km2-footer,.km2-grab,.km2-sheet-head{display:none}
.km2-sheet-scroll{display:contents}

/* ───────────────────────── mobile ≤760 ───────────────────────── */
@media (max-width:760px){
  .km2{--panel-w:100%; --sheet-h:40%; --nav-h:64px; --grid:rgba(160,90,10,.20)}

  /* ─── touch-friendly primitive overrides (bigger hit targets, 16px inputs) ─── */
  /* focus: keyboard-only amber ring (brand) */
  .km2 :focus-visible{outline:2px solid var(--accent); outline-offset:2px}
  .km2 :focus:not(:focus-visible){outline:none}

  .km2-btn{
    font-size:13px; padding:10px 15px; border-radius:9px; min-height:44px;
  }
  .km2-btn svg{width:16px; height:16px}
  .km2-btn-primary:active{background:var(--accent-deep); border-color:var(--accent-deep)}

  .km2-field{gap:7px}
  .km2-label{font-size:10.5px}
  .km2-readout{font-size:12px}

  .km2-input,.km2-select{font-size:16px; padding:11px 12px; min-height:44px} /* no iOS zoom */
  .km2-select{padding-right:32px; background-position:right 12px center}

  .km2-range{margin:16px 0}
  .km2-range::-webkit-slider-thumb{width:26px; height:26px; border:3px solid var(--bg-3); box-shadow:0 1px 4px rgba(20,12,4,.3)}
  .km2-range::-moz-range-thumb{width:26px; height:26px; border:3px solid var(--bg-3); box-shadow:0 1px 4px rgba(20,12,4,.3)}

  .km2-check{gap:11px; min-height:44px}
  .km2-check input{width:20px; height:20px}

  .km2-seg{padding:3px}
  .km2-seg button{font-size:13px; padding:9px 14px; border-radius:7px; min-height:38px}

  .km2-top{padding-top:env(safe-area-inset-top)}
  .km2-body{flex-direction:column; overflow:hidden; position:relative}
  /* the old floating rail pill (brittle `top:calc(stageHeight - 62px)` hack,
     overlapped the canvas) is fully replaced on mobile by the edge-pinned
     clusters below + the bottom category nav. The stage now flexes to fill
     whatever room is left above the fixed-height sheet (claude.design's
     redesign), instead of a fixed vh-based height. */
  .km2-stage{flex:1 1 auto; min-height:150px; height:auto}
  .km2-rail{display:none}
  .km2-objects{display:none}         /* objects panel folds away on phone */
  .km2-rolegrid{grid-template-columns:1fr}
  .km2-top .km2-download-top{display:none}   /* the footer's Order CTA is the persistent one on mobile */

  /* ── the FOCUSED SHEET: .km2-panel plays claude.design's .km2-sheetwrap
     role (fixed height, rounds up over the canvas, grab handle + title/sub
     pinned above ONE scrolling body) — reusing the existing desktop
     Color/Plate panel wholesale instead of duplicating its four
     .km2-sheet[data-km-cat] sections into a second DOM location. ── */
  .km2-panel{
    flex:0 0 auto; width:100%; height:var(--sheet-h); min-height:0;
    position:relative; z-index:20; overflow:hidden;
    border-left:none; border-top:none; background:var(--bg-2);
    border-radius:18px 18px 0 0; margin-top:-14px;
    box-shadow:0 -10px 30px -18px rgba(20,12,4,.4);
    display:flex; flex-direction:column;
  }
  .km2-grab{display:grid; flex-shrink:0; height:22px; place-items:center; cursor:grab; touch-action:none}
  .km2-grab::before{content:''; width:38px; height:4px; border-radius:2px; background:var(--line-strong)}
  .km2-grab:active{cursor:grabbing}
  .km2-sheet-head{
    display:flex; flex-shrink:0; align-items:center; justify-content:space-between;
    gap:10px; padding:2px 18px 12px;
  }
  .km2-sheet-title{font-family:'Space Grotesk',sans-serif; font-weight:600; font-size:18px; letter-spacing:-.02em; color:var(--fg)}
  .km2-sheet-sub{font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.1em; text-transform:uppercase; color:var(--fg-dim-2)}
  .km2-sheet-scroll{
    display:flex !important; flex-direction:column; flex:1; min-height:0;
    overflow-y:auto; -webkit-overflow-scrolling:touch; overscroll-behavior:contain;
  }
  /* the desktop Color/Plate tab switcher is superseded by the bottom nav on
     mobile — both tabpanels stay rendered (never [hidden]) so every
     .km2-sheet inside either one can be shown by category regardless of
     which desktop tab was last active. */
  .km2-tabs{display:none}
  .km2 .km2-tabpanel[hidden]{display:flex !important}
  .km2-tabpanel{display:flex; flex-direction:column}

  /* mobile category sheets (one .km2-sheet shown per bottom-nav item) — the
     outer horizontal/vertical rhythm now lives here instead of on every
     .km2-sec, so a sheet's sections read as one tight group under the
     shared header above (closer to claude.design's .km2-group model). */
  .km2-sheet{padding:16px 18px 20px; display:flex; flex-direction:column; gap:20px}
  .km2-sec{padding:0; gap:12px}
  .km2-sec + .km2-sec{border-top:1px solid var(--line); padding-top:18px; margin-top:2px}

  /* ── edge-only floating stage controls (never over the model) ── */
  .km2-edge{position:absolute; z-index:14; display:flex; gap:8px}
  .km2-edge-tl{top:12px; left:12px; flex-direction:column; align-items:flex-start}
  .km2-edge-tr{top:12px; right:12px; flex-direction:column; align-items:flex-end}
  .km2-edge-br{right:12px; bottom:12px; flex-direction:column; align-items:flex-end}

  /* frosted chip base, shared by every edge control (added as a second class
     alongside each control's own class in keychain-maker.hbs) */
  .km2-chip{
    background:color-mix(in srgb, var(--bg-3) 82%, transparent);
    -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
    border:1px solid var(--line); border-radius:10px;
    box-shadow:0 6px 18px -12px rgba(20,12,4,.5);
  }

  .km2-dims{
    position:static; font-family:'JetBrains Mono',monospace; font-size:12.5px; color:var(--fg);
    padding:8px 12px; letter-spacing:.01em; white-space:nowrap;
  }
  .km2-dims b{color:var(--accent); font-weight:500}

  /* dims on/off toggle (Makerwords-style switch) — toggles [data-km-dims]'s
     `hidden` in keychain-maker.js, which also flips this button's OWN
     .is-active class (not a root[data-dims] attribute like the reference
     mock — same visual result, different state hook). */
  .km2-dimtoggle{
    position:static; display:flex; align-items:center; gap:9px;
    padding:7px 10px 7px 12px; cursor:pointer; user-select:none;
  }
  .km2-dimtoggle span{font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:.12em; text-transform:uppercase; color:var(--fg-dim-2)}
  .km2-switch{position:relative; width:38px; height:22px; flex-shrink:0; border-radius:999px; background:var(--line-strong); border:none; cursor:pointer; padding:0; transition:background .2s}
  .km2-switch i{position:absolute; top:2px; left:2px; width:18px; height:18px; border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.3); transition:transform .22s cubic-bezier(0.34,1.4,0.64,1)}
  .km2-dimtoggle.is-active .km2-switch{background:var(--accent)}
  .km2-dimtoggle.is-active .km2-switch i{transform:translateX(16px)}

  .km2-viewtoggle{position:static; right:auto; bottom:auto; display:inline-flex; padding:3px; gap:2px; box-shadow:none}
  .km2-viewtoggle button{
    font-family:'JetBrains Mono',monospace; font-size:11px; letter-spacing:.08em; font-weight:500;
    color:var(--fg-dim); background:transparent; border:none; padding:7px 12px; border-radius:7px;
    cursor:pointer; min-height:34px; min-width:42px;
  }
  .km2-viewtoggle button:hover{background:transparent; color:var(--fg-dim)}
  .km2-viewtoggle button.is-active{background:var(--accent); color:#fff}

  /* zoom cluster − / fit / + (bottom-right, vertical) */
  .km2-canvasctl{display:flex; flex-direction:column; overflow:hidden; padding:2px}
  .km2-canvasctl button{
    width:42px; height:42px; display:grid; place-items:center; border:none; background:transparent;
    color:var(--fg-dim); cursor:pointer; border-radius:8px;
  }
  .km2-canvasctl button:hover{background:transparent; color:var(--fg-dim)}
  .km2-canvasctl button:active{background:var(--bg-4); color:var(--accent)}
  .km2-canvasctl svg{width:19px; height:19px}
  .km2-canvasctl button + button{border-top:1px solid var(--line)}

  /* view tag (brand motif) — bottom-left on mobile (top-right on desktop) */
  .km2-viewtag{
    top:auto; right:auto; left:12px; bottom:12px;
    font-size:9.5px; letter-spacing:.14em; padding:5px 8px;
  }

  /* too-big warning — stage bottom overlay */
  .km2-warn{
    left:12px; right:12px; bottom:12px; top:auto; max-width:none;
    padding:12px 40px 12px 14px; border-radius:12px; gap:10px; align-items:flex-start;
    box-shadow:0 8px 22px -14px rgba(20,12,4,.5);
  }
  .km2-warn-x{width:26px; height:26px; font-size:17px}

  /* on-object popups — frosted/rounder chrome closer to claude.design's Pill
     popup; the internal head+close-x+slider structure is unchanged (a full
     icon-toolbar/stepper rebuild, matching the mock's exact interaction
     pattern, is a further visual pass — see the note on .km2-objpop in
     keychain-maker.hbs). */
  .km2-att-popup,.km2-objpop{
    background:color-mix(in srgb, var(--bg-3) 94%, transparent);
    -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
    border:1px solid var(--line-strong); border-radius:16px;
    box-shadow:0 14px 34px -14px rgba(20,12,4,.55), 0 2px 0 rgba(255,255,255,.4) inset;
    animation:km2-pop-in .16s ease;
  }
  /* The popup lives inside .km2-stage, which is `overflow:hidden`, and on a
     phone the stage is only as tall as what is left after the sheet — so a tall
     popup is simply CUT OFF at the bottom, taking Remove element with it. The
     lamp's element popup grew past that point when the rotation controls and
     the Center button landed (measured on a 390×844 phone: 522 px of popup in
     a ~476 px stage). Cap it against the stage and let it scroll.
     `touch-action:pan-y` keeps that scroll working while the grab bar inside it
     still claims its own drag — .km2-objpop-grab sets `touch-action:none` on
     itself, so a touch that starts on the head MOVES the card and a touch that
     starts anywhere else scrolls it. */
  .km2-objpop{
    max-height:calc(100% - 24px); overflow-y:auto; touch-action:pan-y;
    overscroll-behavior:contain;
  }

  /* ── footer: Order CTA + bottom category nav ── */
  .km2-footer{
    display:flex; flex-direction:column; flex-shrink:0; position:relative; z-index:25;
    background:var(--bg-2); border-top:1px solid var(--line);
  }
  .km2-actionbar{padding:10px 16px 0}
  .km2-actionbar .km2-btn{width:100%; min-height:50px; font-size:14.5px; border-radius:11px}
  .km2-actionbar .km2-btn svg{width:18px; height:18px}

  .km2-bottomnav{
    display:grid; grid-template-columns:repeat(4,1fr); gap:2px;
    padding:8px max(8px, env(safe-area-inset-left)) calc(8px + env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-right));
  }
  .km2-navitem{
    position:relative; flex-direction:column; gap:4px;
    min-height:52px; padding:6px 4px; border-radius:12px;
    font-size:11px; font-family:'JetBrains Mono',monospace; letter-spacing:.04em;
    color:var(--fg-dim-2); transition:background .16s,color .16s;
  }
  .km2-navitem svg{width:23px; height:23px; stroke-width:1.6}
  .km2-navitem.is-active{color:var(--accent); background:var(--accent-soft)}

  /* ── filament swatches: round, bigger for touch (Pill/round direction) ── */
  .km2-swatch{width:52px; height:52px; border-radius:50%}
  .km2-add-color{width:52px; height:52px; border-radius:50%}
  .km2-eyedrop{width:44px; height:44px}

  /* ── attachments (hook) list — bigger rows for touch ── */
  .km2-att-item{min-height:60px; border-radius:12px; padding:11px 12px}
  .km2-att-badge{width:40px; height:40px; border-radius:9px}
  .km2-att-badge svg{width:22px; height:22px}
  .km2-att-del{width:40px; height:40px; border-radius:9px}
  .km2-att-del svg{width:18px; height:18px}
  .km2-att-name{font-size:14px}
  .km2-att-sub{font-size:10px}

  /* ── background-shape picker (touch-sized, Pill direction): applies to the
     Shape sheet's 5 chips via the added .km2-shape class (see
     keychain-maker.hbs) — the classless import-modal picker keeps the base
     .km2-shape-picker button styling below plus its own -lg 3-col rule. ── */
  .km2-shape{
    display:flex; flex-direction:column; align-items:center; gap:7px; padding:11px 4px 9px;
    border:1px solid var(--line-strong); border-radius:11px; background:var(--bg-3); cursor:pointer;
    color:var(--fg-dim); transition:border-color .13s,background .13s,color .13s;
  }
  .km2-shape svg{width:26px; height:26px; stroke-width:1.5}
  .km2-shape span{font-size:10.5px; font-weight:500; color:var(--fg-dim-2)}
  .km2-shape.is-active{border-color:var(--accent); background:var(--accent-soft); color:var(--accent); font-weight:600}
  .km2-shape.is-active span{color:var(--accent)}
  .km2-shape-picker-lg{grid-template-columns:repeat(3,1fr)}

  .km2-ordergrid{grid-template-columns:1fr}
}

/* ─────────────────── background-shape picker ─────────────────── */
.km2-shape-picker{
  display:grid; grid-template-columns:repeat(5,1fr); gap:6px; width:100%;
}
.km2-shape-picker button{
  display:flex; flex-direction:column; align-items:center; gap:5px;
  padding:8px 2px 6px; border:1px solid var(--line-strong); border-radius:9px;
  background:var(--bg-3); color:var(--fg-dim); cursor:pointer;
  font-size:10.5px; font-weight:500; line-height:1;
  transition:background .13s,color .13s,border-color .13s;
}
.km2-shape-picker button svg{width:22px; height:22px}
.km2-shape-picker button:hover{border-color:var(--accent); color:var(--accent)}
.km2-shape-picker button.is-active{
  border-color:var(--accent); color:var(--accent);
  background:var(--accent-soft); font-weight:600;
}

/* ───────────────────────── import modal ───────────────────────── */
.km2-modal{position:fixed; inset:0; z-index:60; display:grid; place-items:center}
.km2-modal[hidden]{display:none}
.km2-modal-backdrop{
  position:absolute; inset:0; background:color-mix(in srgb,var(--fg) 22%,transparent);
  backdrop-filter:blur(2px);
}
.km2-modal-card{
  position:relative; width:min(440px,calc(100vw - 32px));
  background:var(--bg-2); border:1px solid var(--line-strong); border-radius:14px;
  padding:18px 18px 16px; box-shadow:0 24px 60px -24px rgba(20,12,4,.55);
  display:flex; flex-direction:column; gap:12px;
}
.km2-modal-head{display:flex; align-items:center; justify-content:space-between}
.km2-modal-title{font-family:'Space Grotesk',sans-serif; font-weight:600; font-size:15px; color:var(--fg)}
.km2-modal-x{
  width:28px; height:28px; border:none; background:transparent; color:var(--fg-dim);
  font-size:19px; line-height:1; border-radius:7px; cursor:pointer;
}
.km2-modal-x:hover{background:var(--bg-4); color:var(--fg)}
.km2-modal-sub{font-size:12px; color:var(--fg-dim); margin:-4px 0 2px}
.km2-shape-picker-lg button{padding:12px 4px 9px; font-size:11.5px}
.km2-shape-picker-lg button svg{width:30px; height:30px}
.km2-modal-foot{display:flex; justify-content:flex-end; gap:8px; margin-top:4px}

/* ─────────────────── SVG colour review (svg-color-modal.js) ─────────────────── */
/* The canvas has no intrinsic size here — its width/height attributes are set
   from clientWidth at paint time — so the box must be given one in CSS or it
   collapses to the 300×150 default and the preview draws into a letterbox. */
.km2-colmodal-preview{
  display:block; width:100%; height:200px; border-radius:10px;
  border:1px solid var(--line); background:var(--bg-3);
}
.km2-colrows{display:flex; flex-direction:column; gap:6px}
.km2-colrow{
  display:flex; align-items:center; gap:10px;
  padding:5px 8px; border-radius:9px; background:var(--bg-3); border:1px solid var(--line);
}
.km2-colrow-swatch{width:26px; height:26px; flex:0 0 auto}
.km2-colrow-hex{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px;
  color:var(--fg); letter-spacing:.02em;
}
/* The filament chips a row is assigned to — numbered exactly as the Filament
   colors picker numbers its own swatches, because "orange 1" has to mean the
   same thing in both places. Pushed right, with the share trailing. */
.km2-colrow-slots{margin-left:auto; display:flex; gap:4px; align-items:center}
/* A row shows ONLY the filament it prints in until it is clicked. Opened, the
   strip lifts onto its own surface so it reads as a list being chosen from
   rather than as five things already true of the row. */
.km2-colrow-slots.is-open{
  padding:3px; margin:-3px; border-radius:9px;
  background:var(--bg); box-shadow:inset 0 0 0 1px var(--line-strong);
}
/* The collapsed chip is a control, so it says so — a caret after the number. */
.km2-slotchip-current::after{
  content:'▾'; position:absolute; right:-9px; bottom:-1px;
  font-size:9px; color:var(--fg-dim); text-shadow:none;
}
.km2-slotchip-current{position:relative; margin-right:9px}
.km2-slotchip{
  width:24px; height:24px; flex:0 0 auto; border-radius:7px; cursor:pointer;
  border:1px solid var(--line-strong); padding:0;
  font:inherit; font-size:11px; font-weight:600; font-variant-numeric:tabular-nums;
  /* The number has to stay legible on ANY filament the customer loads, so it is
     white with a dark halo rather than a colour chosen against one ground. */
  color:#fff; text-shadow:0 0 2px rgba(0,0,0,.85), 0 1px 1px rgba(0,0,0,.6);
}
.km2-slotchip:hover{border-color:var(--accent)}
.km2-slotchip.is-active{border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-soft)}
.km2-slotchip-add{
  background:var(--bg-4); color:var(--fg-dim); text-shadow:none; font-size:15px; line-height:1;
}
.km2-slotchip-add:hover{color:var(--accent)}
/* Tabular and fixed-width so the shares line up as a column rather than
   drifting with each row's chip count. */
.km2-colrow-share{width:34px; text-align:right; font-size:12px; color:var(--fg-dim); font-variant-numeric:tabular-nums}
.km2-colmodal-hint{font-size:12px; color:var(--accent); margin:0}
.km2-colmodal-hint[hidden]{display:none}
.km2-colmodal-foot{justify-content:space-between; align-items:center}
.km2-colmodal-actions{display:flex; gap:8px}
.km2-colmodal-reset{
  border:none; background:transparent; padding:4px 2px; cursor:pointer;
  font:inherit; font-size:12px; color:var(--fg-dim); text-decoration:underline;
}
.km2-colmodal-reset:hover{color:var(--fg)}

/* ───────────────────────── order form ───────────────────────── */
.km2-ordergrid{display:grid; grid-template-columns:1fr 1fr; gap:10px}
.km2-ofield{display:flex; flex-direction:column; gap:4px; font-size:12px; color:var(--fg-dim)}
.km2-ofield-full{margin-top:10px}
.km2-ofield > span{font-weight:500}
.km2-ofield .km2-input{width:100%}
.km2-ofield textarea.km2-input{resize:vertical; min-height:56px; font:inherit}
.km2-consent{display:flex; gap:9px; align-items:flex-start; margin-top:14px; font-size:12.5px; color:var(--fg); line-height:1.45; cursor:pointer}
.km2-consent input{margin-top:2px; flex-shrink:0; width:16px; height:16px; accent-color:var(--accent)}
.km2-consent a{color:var(--accent)}
.km2-order-error{margin:10px 0 0; font-size:12.5px; color:var(--warn); background:color-mix(in srgb,var(--warn) 10%,transparent); border:1px solid color-mix(in srgb,var(--warn) 35%,transparent); border-radius:8px; padding:8px 10px}
.km2-order-ok{display:flex; flex-direction:column; align-items:center; text-align:center; gap:6px; padding:14px 6px 6px}
.km2-order-ok svg{width:44px; height:44px; color:var(--accent)}
.km2-order-ok h3{font-family:'Space Grotesk',sans-serif; font-weight:600; font-size:17px; margin:4px 0 0; color:var(--fg)}
.km2-order-ok p{font-size:13px; color:var(--fg-dim); margin:0 0 8px; max-width:320px}

/* reduced motion */
@media (prefers-reduced-motion:reduce){
  .km2 *{transition:none !important; animation:none !important}
}

/* ── maker hub landing ─────────────────────────────────────────── */
.km2--hub{position:fixed;inset:0;overflow-y:auto;}
.hub-main{max-width:1040px;margin:0 auto;padding:48px 24px 64px;}
.hub-eyebrow{font-family:var(--font-mono,monospace);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);}
.hub-heading{font-size:clamp(28px,5vw,44px);margin:10px 0 8px;color:var(--fg);}
.hub-sub{max-width:560px;color:var(--fg-dim);line-height:1.55;}
.hub-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px;margin-top:36px;}
.hub-card{display:flex;flex-direction:column;gap:8px;padding:22px;border:1px solid var(--line);border-radius:10px;background:var(--bg-3);text-decoration:none;color:var(--fg);transition:border-color .15s ease, transform .15s ease;}
a.hub-card:hover{border-color:var(--accent);transform:translateY(-2px);}
.hub-card-name{font-size:18px;font-weight:600;}
.hub-card-desc{color:var(--fg-dim);font-size:14px;line-height:1.5;flex:1;}
.hub-card-cta{color:var(--accent);font-size:14px;}
.hub-card--soon{opacity:.6;}
.hub-card--soon .hub-card-cta{color:var(--fg-dim-2);}

/* ── lamp maker extras ─────────────────────────────────────────── */
.km2-hint{font-size:12px;color:var(--fg-dim-2);line-height:1.5;margin:0;}
.km2-estimate{font-size:14px;color:var(--fg);margin:0;}
.km2-shape small{display:block;font-size:10px;color:var(--fg-dim-2);}
/* both chip grids flow with the MANIFEST-driven list (validateLampManifest
   caps neither designs nor sizes) — auto-fit instead of a hard column count,
   so a third size never renders as a half-width orphan row */
[data-lm-depths]{grid-template-columns:repeat(auto-fit,minmax(96px,1fr));}
[data-lm-designs]{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:8px;}
/* ── award maker: the two part pickers ─────────────────────────── */
/* Same manifest-driven case (validateAwardManifest caps neither bases nor
   tops), but the chips carry a WORD label instead of an icon, so the base
   picker's 5 columns are wrong twice over: one shipped base renders as a 60 px
   chip with four empty cells beside it, and a name that is a real word —
   "Постамент" on the Bulgarian host — does not fit 60 px at all. */
[data-aw-bases],[data-aw-tops]{grid-template-columns:repeat(auto-fit,minmax(96px,1fr));}
@media (max-width:760px){
  /* On mobile .km2-sec collapses to padding:0 with the rhythm reassigned to
     .km2-sheet (see the ≤760 block) — a wrapper this page doesn't have (its
     sections sit directly in .km2-sheet-scroll), so without this the panel
     sections sit flush against the phone's screen edges. Scoped to
     #lamp-maker: the keychain's sheet-scroll contains .km2-sheet groups that
     already carry this exact padding, and must not gain a second layer. */
  #lamp-maker .km2-sheet-scroll{padding:16px 18px 20px;}
}
