  /* Theme tokens (--bg, --surface, --ink, --accent …) are injected by
     src/themes.js in <head> — edit colours there, not here. */
  /* UI typography scale — semantic font-size tokens. ONE knob (--ui-fs) scales ALL UI text;
     edit a type's base px to retune just that type everywhere. Chat-canvas text is separate
     (driven by genCSS vars --msz/--chipsz, never these). */
  :root{
    --ui-fs: 1;                                /* ◀ global UI text scale knob (1 = default) */
    --fs-title:   calc(18px * var(--ui-fs));   /* app brand · popup titles */
    --fs-section: calc(13px * var(--ui-fs));   /* section / group headers (uppercase) */
    --fs-label:   calc(16px * var(--ui-fs));   /* control labels · selects */
    --fs-button:  calc(15px * var(--ui-fs));   /* buttons · segments */
    --fs-body:    calc(17px * var(--ui-fs));   /* body text · steps */
    --fs-hint:    calc(14px * var(--ui-fs));   /* hints · captions · values */
    --fs-micro:   calc(11px * var(--ui-fs));   /* tiny tags · swatch labels · icons */
    --fs-mono:    calc(13px * var(--ui-fs));   /* code / url fields */
  }
*{box-sizing:border-box}
  /* themed scrollbars (match the app, not the browser default) — follows light/dark */
  *{scrollbar-width:thin;scrollbar-color:var(--line-2) transparent}
  ::-webkit-scrollbar{width:11px;height:11px}
  ::-webkit-scrollbar-track{background:transparent}
  ::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:9px;border:3px solid transparent;background-clip:padding-box}
  ::-webkit-scrollbar-thumb:hover{background:var(--ink-3)}
  ::-webkit-scrollbar-corner{background:transparent}
  html,body{height:100%}
  /* Loading splash — covers the page while JS initialises; .fade triggers the exit transition.
     To swap the spinner for a logo image later: replace .splash-spin with an <img> inside .splash-inner. */
  #splash{position:fixed;inset:0;background:var(--bg);display:flex;align-items:center;justify-content:center;z-index:9999;transition:opacity .25s ease}
  #splash.fade{opacity:0;pointer-events:none}
  .splash-spin{width:48px;height:48px;border-radius:50%;border:4px solid var(--line-2);border-top-color:var(--accent);animation:splash-spin .7s linear infinite}
  @keyframes splash-spin{to{transform:rotate(360deg)}}
  body{margin:0;background:var(--bg);color:var(--ink);
    font-family:'Satoshi','Inter','Noto Sans JP',system-ui,sans-serif;font-size:var(--fs-body);
    -webkit-font-smoothing:antialiased;display:flex;flex-direction:column}
  button{font-family:inherit;cursor:pointer;color:inherit}

  .topbar{height:54px;display:flex;align-items:center;gap:14px;padding:0 18px;
    background:var(--surface);flex:none}
.brand{font-weight:700;letter-spacing:-.02em;font-size:var(--fs-title);display:flex;align-items:center;gap:8px}
  .brand .dot{width:10px;height:10px;border-radius:50%;background:var(--accent)}
  .spacer{flex:1}
.tb-btn{border:1px solid var(--line-2);background:var(--surface);border-radius:10px;
    padding:7px 12px;font-size:var(--fs-button);color:var(--ink-2);display:flex;align-items:center;gap:6px}
  .tb-btn:hover{border-color:var(--accent);color:var(--ink)}
  /* greyed when there's no history to act on (undo/redo/reset) */
  .tb-btn:disabled{opacity:.38;cursor:default;color:var(--ink-3);border-color:var(--line)}
  .tb-btn:disabled:hover{border-color:var(--line);color:var(--ink-3)}
  /* icon-only history buttons (Undo/Redo/Reset): compact square, no text label */
  .ico-btn{padding:5px 8px;font-size:15px;line-height:1;justify-content:center;gap:0}
  /* fixed min-width so selects don't resize between languages (CJK is narrower) */
  .sidebar select{min-width:148px}
  .cta{background:var(--accent);color:var(--accent-ink);border:none;font-weight:600;
    padding:8px 16px;border-radius:10px;font-size:var(--fs-button)}
  .cta:hover{filter:brightness(1.08)}

  .ws{flex:1;display:flex;min-height:0}
  /* rail is the deeper bg; each section is a discrete rounded "card" with gaps
     (Chroneco-cupcake feel) — airy, clearly separated, not hairline-packed */
  .sidebar{width:380px;flex:none;border-right:1px solid var(--line);overflow-y:auto;background:var(--bg);padding:10px}
  .hint{font-size:var(--fs-hint);color:var(--ink-3);margin:2px 0 10px}
  .gallery{display:grid;grid-template-columns:1fr 1fr;gap:9px}
  .tile{border:1px solid var(--line-2);border-radius:11px;padding:9px;cursor:pointer;background:var(--surface);
    transition:transform .12s,border-color .12s,box-shadow .12s}
  .tile:hover{transform:translateY(-2px);box-shadow:0 6px 16px rgba(0,0,0,.06)}
  .tile.sel{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}
  #customTile{border-style:dashed}   /* a one-off link-loaded look, not a built-in preset — see setCustomLook() */
  /* mini = a tiny chat-message mockup in the preset's colors (avatar + name pill + bubble with
     text lines), staged on a transparency checker like the canvas — reads as "your OBS overlay". */
  .tile .mini{border-radius:8px;padding:8px;display:flex;align-items:flex-start;gap:6px;overflow:hidden;
    border:1px solid var(--line);background-color:var(--bg);
    background-image:repeating-conic-gradient(var(--line) 0% 25%,transparent 0% 50%);background-size:12px 12px}
  .tile .m-av{width:13px;height:13px;border-radius:50%;background:var(--ink-3);flex:none}
  .tile .m-col{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
  .tile .m-chip{width:44%;height:9px;border-radius:5px}
  .tile .m-bub{min-height:22px;padding:5px 7px;display:flex;flex-direction:column;gap:3px;justify-content:center;
    box-shadow:0 1px 3px rgba(0,0,0,.10)}
  /* mode:'band' preset → the mini shows one full-width band row instead of a bubble */
  .tile .m-band{flex:1;display:flex;gap:6px;padding:6px 7px;align-items:flex-start;box-shadow:0 1px 3px rgba(0,0,0,.10)}
  .tile .mini i{display:block;height:3px;border-radius:2px;opacity:.8}
  .tile .mini i:nth-of-type(1){width:88%}
  .tile .mini i:nth-of-type(2){width:58%}
  .tile .lbl{font-size:var(--fs-label);margin-top:7px;color:var(--ink-2);font-weight:500;text-align:center}
  .tile.sel .lbl{color:var(--ink);font-weight:600}
  .row{display:flex;align-items:center;justify-content:space-between;margin:12px 0;gap:12px}
  .row label{font-size:var(--fs-label);color:var(--ink);flex-shrink:0;white-space:nowrap}
  .sidebar .row select{flex:1;min-width:0}
  /* Quick-panel standard label column: one fixed width + gap across the static .row rows (Font & text
     size, etc.) AND the generated .pr rows, so every slider starts at the same x and is the same length
     (was ragged — .row labels were natural-width, .pr labels min-width:100, gaps 12 vs 14). */
  .sidebar .row,.sidebar .msec .body .pr{gap:12px}
  .sidebar .row>label,.sidebar .msec .body .pr>span:first-child{flex:none;width:128px;min-width:128px}
  #layoutSeg button{padding:6px 9px;font-size:var(--fs-button)}
  /* sidebar controls match the canvas-menu components (swatch size, slider + number box) */
  input[type=color]{width:30px;height:30px;border:1px solid var(--line-2);border-radius:8px;background:none;padding:0;cursor:pointer}
  select,input[type=range]{accent-color:var(--accent)}
  select{border:1px solid var(--line-2);border-radius:10px;padding:5px 8px;background:var(--surface);color:var(--ink);font-size:var(--fs-label)}
  input[type=range]{width:130px}
  .seg{display:inline-flex;border:1px solid var(--line-2);border-radius:10px;overflow:hidden}
  .seg button{border:none;background:var(--surface);padding:6px 12px;font-size:var(--fs-button);color:var(--ink-2);display:flex;align-items:center;justify-content:center;gap:5px}
  .seg button.on{background:var(--accent);color:var(--accent-ink);font-weight:600}
  .seg button:disabled{opacity:.4;cursor:not-allowed}
  /* Corner picker — a card shaped like the bubble with a small square HANDLE at each of the 6 spots
     (corners + top/bottom-centre), like selection handles on a box. No arrows, no labels. */
  .cornpad{position:relative;width:100%;height:84px;margin:8px 0;border:2px solid var(--line-2);border-radius:8px;background:transparent}
  .cornpad button{position:absolute;width:15px;height:15px;padding:0;border:2px solid var(--line-2);border-radius:3px;
    background:var(--surface);cursor:pointer;transition:background .12s,border-color .12s}
  .cornpad button:hover:not(:disabled){border-color:var(--accent)}
  /* editing layer = solid accent (movable); partner layer = same accent, dimmed. The partner is locked
     (not-allowed) only while it shows on the bubble; when it's off you can still click to overlap it. */
  .cornpad button.on{background:var(--accent);border-color:var(--accent)}
  .cornpad button.partner{background:var(--accent);border-color:var(--accent);opacity:.3}
  .cornpad button.partner:disabled{cursor:not-allowed}
  .cornpad button.partner:not(:disabled){opacity:.45;cursor:pointer}
  .cornpad .cp-tl{top:-8px;left:-8px}
  .cornpad .cp-tc{top:-8px;left:50%;transform:translateX(-50%)}
  .cornpad .cp-tr{top:-8px;right:-8px}
  .cornpad .cp-bl{bottom:-8px;left:-8px}
  .cornpad .cp-bc{bottom:-8px;left:50%;transform:translateX(-50%)}
  .cornpad .cp-br{bottom:-8px;right:-8px}
  .url-field{width:100%;box-sizing:border-box;font-size:var(--fs-mono);font-family:ui-monospace,monospace;line-height:1.5;border:1px solid var(--line-2);border-radius:8px;padding:6px 9px;background:var(--surface);color:var(--ink);resize:vertical}
  .url-field::placeholder{color:var(--ink-3)}
  /* image-URL field with inline preview (thumbnail + ✕ remove), used for avatar + decoration links */
  .imgurl-wrap{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px}
  .img-prev{display:flex;align-items:center;gap:8px}
  .img-del{flex-shrink:0;width:24px;height:24px;border-radius:6px;border:1px solid var(--line-2);background:var(--surface);color:var(--ink-3);cursor:pointer;font-size:12px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0}
  .img-del:hover{background:var(--accent-soft);color:var(--ink);border-color:var(--accent)}
  /* badge thumbnails: each gets a corner ✕ to remove just that URL line */
  .badge-thumb{position:relative;display:inline-flex}
  .badge-thumb-img{width:38px;height:38px;border-radius:50%;object-fit:cover;background:var(--surface-2);border:1px solid var(--line-2)}
  .badge-thumb-img.broken{opacity:.35}
  .badge-thumb-del{position:absolute;top:-5px;right:-5px;width:17px;height:17px;border-radius:50%;border:1px solid var(--line-2);background:var(--surface);color:var(--ink-3);cursor:pointer;font-size:10px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0}
  .badge-thumb-del:hover{background:var(--accent-soft);color:var(--ink);border-color:var(--accent)}
  .lay-hd{font-size:var(--fs-section);font-weight:600;color:var(--ink-3);text-transform:uppercase;letter-spacing:.07em;margin-bottom:5px}
  /* 2-up button grid — used by the static Layout section (#layoutSeg Direction / #alignSeg Alignment). */
  .lay-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:6px}
  .lay-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:10px 6px;
    border:1.5px solid var(--line-2);border-radius:10px;background:var(--surface);
    color:var(--ink-2);cursor:pointer;font-family:inherit;font-size:var(--fs-button);font-weight:600;
    transition:border-color .15s,background .15s,color .15s}
  .lay-btn svg{width:32px;height:26px}
  .lay-btn:not(.on):hover{border-color:var(--accent);color:var(--ink)}
  .lay-btn.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}
  .tgl{position:relative;width:38px;height:22px;border-radius:11px;background:var(--line-2);border:none;transition:background .15s;flex:none}
  .tgl.on{background:var(--accent)}
  .tgl::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:left .15s}
  .tgl.on::after{left:18px}
  .tgl-wrap{display:inline-flex;align-items:center;gap:6px}
  .tgl-state{font-size:10px;font-weight:700;letter-spacing:.04em;color:var(--ink-3);min-width:20px;text-align:right}
  .tgl-state.on{color:var(--accent)}
  .tgl:disabled{opacity:.4;cursor:not-allowed}

  .stagewrap{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--stage-bg);position:relative}
  .replay{border:1px solid var(--line-2);background:var(--surface);border-radius:10px;padding:7px 12px;font-size:var(--fs-button);color:var(--ink-2)}
  .replay[disabled]{opacity:.35;cursor:default}

  /* stage = flex-row: left .canvas-area (pan/zoom viewport) + right #pipWin panel */
  .stage{flex:1;display:flex;flex-direction:row;min-height:0}
  /* canvas-area: the actual pan/zoom viewport (old .stage role) */
  .canvas-area{flex:1;position:relative;overflow:hidden;min-height:0}
  .canvas-area.grabbing{cursor:grabbing}
  .canvas-area.zoomdrag{cursor:ew-resize}
  .canvas-wrap{position:absolute;top:0;left:0;flex:none;border-radius:14px;overflow:hidden;box-shadow:0 12px 44px rgba(0,0,0,.18)}
  /* backdrop lives on the (non-transformed) wrap → constant transparency grid, area follows canvas size/aspect */
  .canvas-wrap.checker{background-color:#e8e5dd;background-image:
    linear-gradient(45deg,#d0ccc0 25%,transparent 25%),linear-gradient(-45deg,#d0ccc0 25%,transparent 25%),
    linear-gradient(45deg,transparent 75%,#d0ccc0 75%),linear-gradient(-45deg,transparent 75%,#d0ccc0 75%);
    background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0}
  .canvas-wrap.white{background:#ededed}   /* soft light (not pure #fff) */
  .canvas-wrap.black{background:#1e2024}   /* soft dark (not pure #000); old 'solid' normalises to this in applyBg */
  /* Background picker = direct colour swatches (Transparent · Light · Dark) */
  .bg-swatches{display:flex;gap:5px}
  .bg-swatches button{width:22px;height:22px;border-radius:6px;border:2px solid var(--line-2);padding:0;cursor:pointer;background-clip:padding-box;flex:none}
  .bg-swatches button:hover{border-color:var(--accent)}
  .bg-swatches button.on{border-color:var(--accent);box-shadow:0 0 0 1.5px var(--accent)}
  .bg-swatches button[data-bg="white"]{background:#ededed}
  .bg-swatches button[data-bg="black"]{background:#1e2024}
  .bg-swatches button[data-bg="checker"]{background-color:#e8e5dd;background-image:
    linear-gradient(45deg,#d0ccc0 25%,transparent 25%),linear-gradient(-45deg,#d0ccc0 25%,transparent 25%),
    linear-gradient(45deg,transparent 75%,#d0ccc0 75%),linear-gradient(-45deg,transparent 75%,#d0ccc0 75%);
    background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}
  .canvas{position:absolute;top:0;left:0;transform-origin:top left;overflow:hidden;border-radius:14px;background:transparent;--canvas-pad:40px}
  /* Edit canvas only — cards appear INSTANTLY (no entry slide-in/fade while you style; it re-played on
     every preset/mode switch). Trick: a big NEGATIVE animation-delay starts the entry (slideinup/fadein,
     fill:both) already past its end, so it renders its FINAL frame immediately = no visible slide — while
     an INFINITE animation like the band glow-pulse keeps running (just phase-shifted). styles.css is NEVER
     part of the OBS export, so the export + the Preview page (#realPreviewItems) keep the real entry. */
  #chatItems > * { animation-delay: -10s !important; }
  /* Returning to Preview re-toggles #realPreviewBody display none→flex, which RE-FIRES every existing
     row's entry animation = a slide-in wave (flicker). Rows present at switch-time are tagged .rp-settled
     to skip that replay (same -10s trick); NEW incoming feed rows have no tag → animate normally. */
  #realPreviewItems > .rp-settled { animation-delay: -10s !important; }
  /* guides (inside the real-px canvas, scale with it) */
  .guides{position:absolute;inset:0;pointer-events:none;display:none;
    --gw:2px; --gd:16px; --gg:10px; --gc:rgba(80,160,255,.5)}   /* dashed-guide thickness / dash / gap / colour (canvas px — lines trace geometry, kept thin+quiet) */
  .canvas.show-guides .guides{display:block}
  /* Dashed rectangle = inner content area (OBS browser source boundary). Thick line + wide dash gap via a
     repeating-linear-gradient (border:dashed can't space the gap); --gw thickness, --gd dash, --gg gap.
     Guides scale with the canvas, so these are canvas px — generous so they read at fit zoom. */
  .g-frame{position:absolute;inset:40px;pointer-events:none;
    background:
      repeating-linear-gradient(90deg,var(--gc) 0 var(--gd),transparent var(--gd) calc(var(--gd) + var(--gg))) left top   /100% var(--gw) no-repeat,
      repeating-linear-gradient(90deg,var(--gc) 0 var(--gd),transparent var(--gd) calc(var(--gd) + var(--gg))) left bottom/100% var(--gw) no-repeat,
      repeating-linear-gradient(180deg,var(--gc) 0 var(--gd),transparent var(--gd) calc(var(--gd) + var(--gg))) left  top/var(--gw) 100% no-repeat,
      repeating-linear-gradient(180deg,var(--gc) 0 var(--gd),transparent var(--gd) calc(var(--gd) + var(--gg))) right top/var(--gw) 100% no-repeat}
  /* Label is chrome, not geometry → counter-scale (--gcs = 1/canvasScale, set in updateCanvas) so it stays
     a constant on-screen size at any zoom — matches the body-level Chat-width grip label. */
  .g-frame::after{content:'OBS screen';position:absolute;top:calc(10px * var(--gcs,1));right:calc(12px * var(--gcs,1));
    font-size:calc(13px * var(--gcs,1));font-weight:700;color:rgba(80,160,255,.7);pointer-events:none}
  /* Single vertical line = right edge of the chat column. Offset by --canvas-pad so it lines up with
     #chatItems (which sits inside .chat's padding), NOT the raw canvas edge — otherwise the line sat
     --canvas-pad too far left and rows/bands appeared to overflow it. */
  .g-band{position:absolute;top:0;bottom:0;left:var(--canvas-pad,40px);width:var(--g-bw,560px);display:none;pointer-events:none;
    background:repeating-linear-gradient(180deg,var(--gc) 0 var(--gd),transparent var(--gd) calc(var(--gd) + var(--gg))) right top/var(--gw) 100% no-repeat}
  .guides.show-band .g-band{display:block}
  .guides.band-right .g-band{left:auto;right:var(--canvas-pad,40px);background-position:left top}
  /* in-canvas band label hidden — replaced by the body-level #widthHandle drag handle (clickable above .chat) */

  /* chat preview — REAL px on the 1080p/720p canvas, var-driven */
  .chat{position:absolute;left:0;right:0;bottom:0;padding:var(--canvas-pad,40px);display:flex;flex-direction:column;gap:0;
    font-family:var(--ffam,'Kanit'),'Noto Sans JP',sans-serif;z-index:1;
    /* Row (Bar) box defaults — own vars, independent of the bubble box. Stylesheet defaults so the
       Row menu swatches/sliders read a value; user edits override via inline style (setVar). */
    --rowbg:#ffffff;--rowbgop:100;--rowbg2:#ff5e7e;--rowga:135deg;--rowbw:0;--rowbc:#C8102E;--rowbcop:100;--rowrad:20px}
  /* decoration = a global image that decorates EACH bubble, driven by CSS vars so one definition applies
     to every message. ANCHOR = a CORNER (6-way): height-independent (bottom:0 is the bottom edge of EVERY
     bubble), so the same corner lands at the same edge on every message — no cross-message drift. */
  /* Decoration — ONE corner image on the bubble (6 corners; mirrors export #message-container::after).
     Corner-only (no free X/Y drag). --dcx/--dcy re-centre the deco onto the chosen edge. */
  .deco{position:absolute;pointer-events:auto;cursor:pointer;z-index:var(--decoz,10);
    width:var(--decosz,32px);height:var(--decosz,32px);opacity:calc(var(--decoop,100)/100);
    display:flex;align-items:center;justify-content:center;line-height:1;font-size:calc(var(--decosz,32px)*.8);
    background:var(--decoimg,url(asset/sample_decorate.png)) center/contain no-repeat;
    top:auto;bottom:auto;left:auto;right:auto;
    transform:translate(var(--dcx,50%),var(--dcy,-50%)) rotate(var(--decorot,0deg));
    filter:drop-shadow(0 2px 6px rgba(0,0,0,.3))}
  #content{position:relative}
  .chat.deco-tr .deco{top:0;right:0;--dcx:50%;--dcy:-50%}     /* default = tr (matches base) */
  .chat.deco-tl .deco{top:0;left:0;--dcx:-50%;--dcy:-50%}
  .chat.deco-tc .deco{top:0;left:50%;--dcx:-50%;--dcy:-50%}
  .chat.deco-bl .deco{bottom:0;left:0;--dcx:-50%;--dcy:50%}
  .chat.deco-bc .deco{bottom:0;left:50%;--dcx:-50%;--dcy:50%}
  .chat.deco-br .deco{bottom:0;right:0;--dcx:50%;--dcy:50%}
  .chat.deco-off .deco{display:none}
  /* In Full/Band mode .deco moves to [data-el="row"] (the renderer) so the canvas gizmo tracks the
     same anchor as genCSS (renderer::before). Ensure the row is a containing block for position:absolute. */
  .chat.rowFull [data-el="row"],.chat.band [data-el="row"]{position:relative;overflow:visible}
  /* Member-badge CORNER is genCSS-only now (renderer/message ::after pseudo per mode). The old .bdgimg
     preview helper + its 6-corner rules were removed 2026-06-26 — they double-rendered on top of it. */
  /* corner-deco entry animation (bounce=scale / swing=rotate via individual transform props, so they
     compose with the position transform). Plays once on render. */
  @keyframes deco-bounce{0%{scale:0}60%{scale:1.15}100%{scale:1}}
  @keyframes deco-swing{0%{rotate:0deg}25%{rotate:14deg}60%{rotate:-10deg}100%{rotate:0deg}}
  .chat.deco-anim-bounce .deco{animation:deco-bounce .5s cubic-bezier(.34,1.56,.64,1) both}
  .chat.deco-anim-swing .deco{animation:deco-swing .5s cubic-bezier(.34,1.56,.64,1) both}
  .chat.align-r{align-items:flex-end}
  /* Horizontal Edit: applyItemsLayout makes #chatItems a bottom/top-docked, viewport-width,
     horizontally-SCROLLABLE strip (overflow-x) at 1:1, and the canvas docks to that edge (clampPan/
     centerCanvas) so it isn't cropped — the user edits each card + scrolls right to reach all. The real
     left-to-right MOTION still plays in Preview (whose justify-end branch is left untouched; banner points
     there). .chat stays column (one child — the actual row flow is on #chatItems). */
  .chat.layout-h{flex-direction:column}
  /* Show-in-chat visibility — mockup uses the real-YT-replica DOM: regular = [data-el="row"][data-role],
     event card = [data-el="card"][data-fam] (NOT the old .msg/.fam-*/.is-card classes, which match nothing). */
  /* #chat (id) + !important: the injected genCSS (#chatEditStyle) sets display:flex!important on the
     renderers, so the hide needs an id-level selector to win the cascade. */
  #chat.hide-chat [data-el="row"][data-role]{display:none!important}   /* [data-role] = extra specificity to beat the @scope genCSS row rule */
  #chat.hide-event [data-el="card"]{display:none!important}
  #chat.hide-viewer [data-el="row"][data-role=viewer],#chat.hide-member [data-el="row"][data-role=member],
  #chat.hide-mod [data-el="row"][data-role=mod],#chat.hide-owner [data-el="row"][data-role=owner],
  #chat.hide-sc [data-fam=superchat],#chat.hide-ss [data-fam=supersticker],
  #chat.hide-mem [data-fam=membership],
  #chat.hide-gift [data-fam=give],#chat.hide-gift [data-fam=recv],
  #chat.hide-jewels [data-fam=gift]{display:none!important}
  /* (The whole v2 mock-DOM stylesheet — .msg/.chip/.bubble/.msgtext/.content/.card/.av/.bdg and every
     band/glow/edge/tail rule scoped to them — was DELETED 2026-07-02i. The Edit canvas has been the
     real-YT-replica DOM (yt-live-chat-*-renderer + data-el) styled by the INJECTED genCSS since
     2026-06-2x, so those classes matched ZERO elements (verified live: querySelectorAll = 0 for each).
     The dead rules cost real review time — during the 2026-07-02 audits every styles.css hit had to be
     re-checked live/dead by hand, and a stale `.chat.band.glow-on .msg` block read as a phantom bug.
     Canvas-only chrome that still paints the replica lives below: `.deco` + `.row-visual` (+ hover/
     selection affordances). Anything look-related belongs in genCSS, never here. */
  /* Edit: top-anchored + internal scroll, so growing a box pushes the rest DOWN and you can
     scroll to reach messages below the frame. Preview keeps the real bottom-anchored live feed. */
  body.mode-showcase .chat{top:0;overflow-y:auto;overflow-x:hidden}
  /* Horizontal Edit: the chat is a stage-height strip ALWAYS docked to the bottom — NOT the full-height
     vertical-scroll workbench above. The row scrolls horizontally on #chatItems (overflow-x); its height =
     the visible stage so the scrollbar sits at the bottom. Cards align top/bottom INSIDE (align-items), so
     anchor-top shows cards up top while the scrollbar stays at the bottom (no mid-canvas clutter). */
  body.mode-showcase .chat.layout-h{top:auto;bottom:var(--canvas-pad,40px);overflow:visible;padding:0}   /* dock to guide-frame edge (inset:40px), not canvas edge */
  body.mode-showcase .chat.layout-h.anchor-top{top:var(--canvas-pad,40px);bottom:var(--canvas-pad,40px)}   /* fill guide frame top→bottom; scrollbar sits at guide-bottom */
  .chat.deco-img .deco{color:transparent}   /* a real image is set → hide the emoji placeholder */
  .chat.deco-img .deco{background:none !important}   /* image via #message-container::after; .deco = click gizmo */
  /* Bar inner border: inset outline on the band row. TWO targets on the canvas:
     - the renderer (carries the band bg + radius; matches the export, shioriExtras puts it here);
     - `.row-visual`, the absolute hover-pop overlay (inset:0, band bg, z-index:0) that COVERS the
       renderer's inset outline — so on the canvas only the .row-visual outline is actually visible.
     The old `.msg` target never matched the live DOM AND the renderer outline was hidden behind
     .row-visual, so the band inner border never showed in Edit (export was always fine, no .row-visual
     in OBS). Own --rowibord* vars (Bubble↔Bar independent). */
  .chat.band.rowibord-on yt-live-chat-text-message-renderer:not([data-fam]),
  /* Opacity via color-mix (owner 2026-07-02i: "Opacity ของ inner border ใน full-width ไม่ทำงาน") —
     this Edit-canvas overlay outline COVERS the injected genCSS outline (which composits opacity via
     toRgba), so reading --rowibordc raw here silently ignored --rowibordop in Edit only.
     Scoped to NORMAL rows only (owner bug 2026-07-02: ".row-visual" exists on CARD replicas too —
     the unscoped selector painted the inner border on every paid card in Edit, while the export/OBS
     correctly leaves cards alone; and the recv replica (a text renderer tagged data-fam=recv) showed
     an outline its real ytd- tag never gets in OBS — :not([data-fam]) keeps Edit == OBS for both). */
  .chat.band.rowibord-on [data-el="row"]:not([data-fam])>.row-visual{outline:var(--rowibordw,2px) var(--rowibords,dashed) color-mix(in srgb,var(--rowibordc,#ffffff) calc(var(--rowibordop,100) * 1%),transparent);outline-offset:calc(-1 * var(--rowibordgap,6px))}
  /* (Bubble-mode layered shadow / tail base / .card family block — all v2 mock rules — deleted
     2026-07-02i, see the mock-DOM note above. Tails/shadows/cards on the replica all come from the
     injected genCSS itself, so Edit == Preview == OBS by construction.) */
  body:not(.ready) [data-el],body:not(.ready) .lay-btn,body:not(.ready) .tile,
  body:not(.ready) .tgl,body:not(.ready) .cr{transition:none!important;animation-duration:0s!important}
  /* hover = thin dashed hint + one-shot scale pop; SELECTED = solid ring */
  @keyframes card-pop{0%,100%{scale:1}45%{scale:1.03}}
  @keyframes card-pop-sm{0%,100%{scale:1}45%{scale:1.08}}
  /* row-visual: absolute overlay for Full/Band pop — scale doesn't affect layout, no reflow */
  .row-visual{position:absolute;inset:0;background:inherit;border-radius:inherit;pointer-events:none;z-index:0}
  /* Event cards: the row-visual inherits the CARD bg, and (positioned, z:0) it painted OVER the card's
     own static header (name + amount), hiding them on the canvas (canvas-only el — fine in OBS). Drop it
     BEHIND the content; it's a background layer for the pop, so it still scales correctly on hover. */
  #chat [data-el="card"] .row-visual{z-index:-1}
  @keyframes row-visual-pop{0%,100%{scale:1}45%{scale:1.05}}
  @keyframes av-pop{0%,100%{scale:1;translate:0 0}45%{scale:1.08;translate:-8px 0}}
  body.mode-showcase #chat .row-hover .row-visual,
  body.mode-showcase #chat .row-hover>*:not(.deco):not(.row-visual):not(yt-img-shadow){animation:row-visual-pop .22s ease-out!important}
  body.mode-showcase #chat .row-hover>yt-img-shadow{animation:av-pop .22s ease-out!important;transform-origin:right center}
  /* Hover affordance = cursor + a one-shot scale pop ONLY. No dashed outline ring
     (owner 2026-06-29: the gray dashed hover ring read as the inner border leaking
     OUTSIDE the element, esp. in band mode). Selection still shows the solid .sel-ring. */
  body.mode-showcase [data-el="bubble"]:hover{cursor:context-menu;animation:card-pop .22s ease-out}
  body.mode-showcase [data-el="card"]:hover{cursor:context-menu}
  body.mode-showcase [data-el="card"]:hover .row-visual{animation:card-pop .22s ease-out}
  body.mode-showcase [data-el="chip"]:hover,
  body.mode-showcase [data-el="av"]:hover{cursor:context-menu;animation:card-pop-sm .22s ease-out}
  body.mode-showcase [data-el="av"]:hover{overflow:visible}
  /* Full/Band: chip+bubble are one visual unit — suppress individual hover; row gets the outline instead */
  body.mode-showcase #chat.band [data-el="chip"]:hover,body.mode-showcase #chat.band [data-el="bubble"]:hover,
  body.mode-showcase #chat.rowFull [data-el="chip"]:hover,body.mode-showcase #chat.rowFull [data-el="bubble"]:hover{outline:none!important;animation:none;cursor:default}
  /* row (Full/Band): cursor only — no dashed hover ring (removed per owner). */
  body.mode-showcase #chat.band .row-hover,
  body.mode-showcase #chat.rowFull .row-hover{cursor:context-menu}
  /* Selection marker for the picked element: crisp solid outline, distinct from hover (dashed) and
     the Glow effect (blurred drop-shadow). Overrides ibord-on outline while selected — acceptable. */
  .sel-ring{outline:2px solid var(--accent)!important;outline-offset:2px}
  /* floating "open menu" button on the selected element — discoverability for right-click */
  .elmenu{position:fixed;z-index:47;display:none;align-items:center;justify-content:center;
    width:26px;height:26px;border-radius:8px;background:var(--accent);color:#fff;
    border:2px solid rgba(255,255,255,.2);box-shadow:0 2px 7px rgba(0,0,0,.35);cursor:pointer;padding:0}
  .elmenu.show{display:flex}
  .elmenu:hover{filter:brightness(1.12)}
  .elmenu svg{width:16px;height:16px;display:block;fill:currentColor}
  /* Preview-feed entry/exit motion now comes from genCSS + shioriExtras (the exported CSS) so
     Preview == OBS; the old mock-feed .msg keyframes here were dead (mode-live hides #chat). */

  .strip{display:flex;align-items:center;gap:16px;padding:10px 18px;border-top:1px solid var(--line);
    background:var(--surface);font-size:var(--fs-hint);color:var(--ink-2);flex-wrap:wrap;flex:none}
  .strip .grp{display:flex;align-items:center;gap:7px}
  .clabel{font-size:var(--fs-hint);color:var(--ink-3);font-variant-numeric:tabular-nums}
  /* Focus selector — Edit mode only: Chat / SC / Sticker / Member / Gift */
  /* compact connected segmented control (saves strip width + cleaner than separate pills) */
  .focus-grp{display:flex;gap:2px;align-items:center;background:var(--surface);border:1px solid var(--line-2);border-radius:9px;padding:2px}
  /* Edit ⇄ Preview switch — 2-state segmented toggle on the bar above the canvas (both modes).
     Switch is instant (no canvas fade — a fade dimmed the live feed = flicker). */
  .modeSwitch button{cursor:pointer;padding:6px 16px;font-family:inherit}   /* lives in the top bar now → a touch tighter to match its height */
  /* Drive active state from body class so the correct button highlights before JS runs
     (avoids a flicker where HTML hardcodes Preview=on even when restoring to Edit). */
  body.mode-live .modeSwitch [data-mode="live"],
  body.mode-showcase .modeSwitch [data-mode="edit"]{background:var(--accent);color:var(--accent-ink);font-weight:600}
  body.mode-live .modeSwitch [data-mode="edit"],
  body.mode-showcase .modeSwitch [data-mode="live"]{background:var(--surface);color:var(--ink-2);font-weight:400}
  .strip-sep{flex:none;width:1px;height:18px;background:var(--line-2);margin:0 2px}
  body.mode-live #layersBtn{display:none}   /* Edit-only strip item hidden in Preview */
  body.mode-live .focus-grp{display:none}
  .focus-btn{font-size:var(--fs-hint);font-weight:600;padding:3px 9px;border-radius:7px;border:none;
    background:transparent;color:var(--ink-3);cursor:pointer;transition:all .12s;white-space:nowrap}
  .focus-btn:hover{color:var(--ink);background:var(--accent-soft)}
  .focus-btn.on{background:var(--accent);color:#fff}
  /* Preview-only strip groups (View/Size) — hidden in Edit mode */
  .strip-pv{display:none;gap:16px}
  body.mode-live .strip-pv{display:contents}
  /* Feed timeline readout — position in the sample loop (Speed control's neighbour) */
  .pv-timeline{position:relative;width:90px;height:6px;border-radius:3px;background:var(--line-2);overflow:hidden}
  .pv-timeline-fill{position:absolute;inset:0 auto 0 0;width:0%;background:var(--accent);border-radius:3px}
  /* Focused Edit ghost system: non-focused card types at 15% opacity ([data-el] / [data-fam] DOM) */
  #chat[class*="focus-"] [data-el="row"],#chat[class*="focus-"] [data-el="card"]{opacity:.15!important;transition:opacity .15s}
  #chat.focus-chat [data-el="row"]{opacity:1!important}
  #chat.focus-sc [data-fam=superchat]{opacity:1!important}
  #chat.focus-ss [data-fam=supersticker]{opacity:1!important}
  #chat.focus-mem [data-fam=membership],
  #chat.focus-mem [data-fam=give],
  #chat.focus-mem [data-fam=recv]{opacity:1!important}
  #chat.focus-gift [data-fam=gift]{opacity:1!important}

  .pop{position:fixed;z-index:50;background:var(--surface);border:1px solid var(--line);
    border-radius:16px;box-shadow:0 1px 2px rgba(0,0,0,.05),0 8px 22px rgba(0,0,0,.10),0 22px 60px rgba(0,0,0,.16);
    padding:18px 20px;width:330px;display:none;max-height:calc(100vh - 16px);overflow-y:auto;overflow-x:hidden}
  @keyframes pop-in{0%{opacity:0;scale:.95;translate:0 -4px}100%{opacity:1;scale:1;translate:0 0}}
  @keyframes subpop-in{0%{opacity:0;translate:-6px 0}100%{opacity:1;translate:0 0}}
  .pop.show{display:block;animation:pop-in .14s ease-out}
  .subpop{width:330px;z-index:51}   /* match .pop so submenu sliders are as long as the main ones (paints over the main panel) */
  .subpop.show{animation:subpop-in .12s ease-out}
  /* inline L1 subgroup section (replaces the L2 floating panel). No manual collapse (owner
     2026-07-02i): shown/hidden is DERIVED from the feature's toggle — ON = all controls visible,
     OFF = header row only. An ON feature with collapsed controls read as a state bug. */
  .sg-acc{border-top:1px solid var(--line);padding-top:4px;margin-top:8px}
  .sg-head{display:flex;align-items:center;gap:6px;padding:5px 0;user-select:none}
  .sg-lbl{font-size:var(--fs-section);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);flex:1}
  .sg-body{display:none;padding:0 0 4px}
  .sg-acc.open .sg-body{display:block}
  .pop .pt{font-size:var(--fs-title);font-weight:700;letter-spacing:-.01em;color:var(--ink);margin:0 0 14px}
  /* submenu header: title on the left, the group's master on/off toggle on the right */
  .subhead{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 0 13px}
  .subhead .pt{margin:0}
  .pop-sep{border-top:1px solid var(--line);margin:10px 2px 4px}
  /* inline primary groups (rendered directly in the main panel, no hover). Owner 2026-07-02:
     group headers used the SAME .pt style as the panel title (18px bold) with only a 1px hairline
     between groups — every section read as "a title", nothing chunked. Now each group is a tinted
     card block (--bg on the --surface panel, works in both themes) and its header drops to the
     sidebar's section-header language (13px uppercase accent) so the hierarchy reads
     title > SECTION > rows at a glance. */
  .pop .pgrp{background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:9px 12px 5px;margin:0 0 10px}
  .pop .pgrp .subhead{margin:0 0 4px}
  .pop .pgrp .subhead .pt{font-size:var(--fs-section);font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--accent)}
  /* grouped main menu: each group opens a submenu to the right */
  .grp-item{display:flex;align-items:center;justify-content:space-between;width:100%;border:none;background:none;
    color:var(--ink);font-family:inherit;font-size:var(--fs-button);padding:11px 12px;border-radius:10px;cursor:pointer;text-align:left}
  .grp-item:hover,.grp-item.on{background:var(--accent-soft);color:var(--accent)}
  /* (no card-pop bounce on hover — the group rows read as a quiet list, not springy buttons) */
  .grp-item .chev{color:var(--ink-3);font-size:var(--fs-button);line-height:1}
  .grp-item.on .chev{color:var(--accent)}
  .pr{display:flex;align-items:center;justify-content:space-between;gap:14px;min-height:42px}
  /* wide controls (segments/arrows/layer) stack full-width so they never crop */
  .pr.stack{flex-direction:column;align-items:stretch;gap:9px;justify-content:center;padding:5px 0}
  .pr.stack>span:first-child{font-size:var(--fs-hint);color:var(--ink-3)}
  /* Tail direction pad — cornpad-style rectangle with handles at 4 edge midpoints */
  .tailpad{position:relative;width:100%;height:56px;margin:8px 0;border:2px solid var(--line-2);border-radius:8px;background:transparent}
  .tailpad button{position:absolute;width:15px;height:15px;padding:0;border:2px solid var(--line-2);border-radius:3px;background:var(--surface);cursor:pointer;transition:background .12s,border-color .12s}
  .tailpad button:hover{border-color:var(--accent)}
  .tailpad button.on{background:var(--accent);border-color:var(--accent)}
  .tailpad .tp-t{top:-8px;left:50%;transform:translateX(-50%)}
  .tailpad .tp-b{bottom:-8px;left:50%;transform:translateX(-50%)}
  .tailpad .tp-l{left:-8px;top:50%;transform:translateY(-50%)}
  .tailpad .tp-r{right:-8px;top:50%;transform:translateY(-50%)}
  .pr-note{font-size:var(--fs-hint);color:var(--ink-3);font-style:italic;line-height:1.4;padding:0 0 7px;
    display:flex;align-items:center;gap:6px}
  .pr-note::before{content:'◉';font-style:normal;font-size:var(--fs-micro);color:var(--accent);opacity:.7;flex:none}
  /* sub-section divider inside a panel section (e.g. Entry / Exit groups) */
  .pr-group{font-size:var(--fs-section);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);
    margin:10px 0 2px;padding-top:12px;border-top:1px solid var(--line)}
  .pr-group:first-child{border-top:none;padding-top:2px;margin-top:2px}
  .msec .body .pr-group+.pr{border-top:none}   /* no double rule right under a group header */
  .pr.stack .seg{display:flex;width:100%}
  .pr.stack .seg button{flex:1;min-width:0;padding:6px 4px;white-space:nowrap}
  .pop .pr+.pr{border-top:1px solid var(--line)}
  .pr>span:first-child{font-size:var(--fs-label);color:var(--ink-2);white-space:nowrap;flex:none;min-width:100px}
  #pop .pr>span:first-child{min-width:108px}
  /* color control = a swatch button; click → the color popover (#colorPop) with HEX field + presets */
  .cr{width:40px;height:30px;border-radius:8px;border:1px solid var(--line-2);cursor:pointer;flex:none;
    box-shadow:inset 0 0 0 1px rgba(255,255,255,.25);transition:border-color .12s,transform .12s}
  .cr:hover{border-color:var(--accent);transform:translateY(-1px)}
  /* direct color-row swatch (label left, colour bar right) — wider than the 40px chip; colour-pair/grid .cr (nested) keep their size */
  .pr>.cr{width:30px;height:30px}   /* canvas menu colour swatch — small square */
  /* color popover (one shared instance, positioned at the clicked swatch) */
  .colorpop{position:fixed;z-index:55;display:none;background:var(--surface);border:1px solid var(--line);
    border-radius:12px;box-shadow:0 14px 38px rgba(0,0,0,.32);padding:12px;width:214px}
  .colorpop.open{display:block}
  .cp-row{display:flex;gap:8px;align-items:center;margin-bottom:10px}
  .colorpop input[type=color]{width:46px;height:34px;border:1px solid var(--line-2);border-radius:8px;background:none;padding:0;cursor:pointer;flex:none;-webkit-appearance:none}
  .colorpop input[type=color]::-webkit-color-swatch-wrapper{padding:0}
  .colorpop input[type=color]::-webkit-color-swatch{border:none;border-radius:6px}
  .cphex{flex:1;min-width:0;border:1px solid var(--line-2);border-radius:8px;background:var(--surface);color:var(--ink);
    padding:7px 9px;font-family:ui-monospace,monospace;font-size:var(--fs-mono);font-weight:600;text-transform:uppercase;text-align:center;letter-spacing:.04em}
  .cphex:focus{outline:none;border-color:var(--accent)}
  .cpswatches{display:grid;grid-template-columns:repeat(8,1fr);gap:5px}
  .cpsw{width:100%;aspect-ratio:1;border:1px solid var(--line-2);border-radius:5px;cursor:pointer;padding:0;transition:transform .1s}
  .cpsw:hover{transform:scale(1.18);border-color:var(--accent)}
  /* Zone 1 mode-aware swatches (5 color slots in the Style section) */
  /* Colours section (cupcake layout): single colours = label ABOVE + chunky swatch + hex; per-role =
     a header + one column per role (Default/Member/Moderator/Owner) with the swatch filling the column. */
  .cfield{margin:12px 0}
  .cfield>label{display:block;font-size:var(--fs-label);color:var(--ink-2);margin-bottom:5px}
  .cfield-row{display:flex;gap:8px;align-items:stretch}
  .cfield-row .cr{width:64px;height:34px;flex:none}
  /* Canvas popup colour swatch — quieter than the sidebar Colors chunky swatch (which keeps 64×34):
     a small square, no white inset ring (just the --line-2 border, reads as an input), so the saturated
     colour is a small dose and the hex field becomes the primary affordance. Scoped to .pop. (advisor) */
  .pop .cfield-row .cr{width:34px;height:34px;box-shadow:none}
  .hexinp{flex:1;min-width:0;background:var(--surface);border:1px solid var(--line-2);border-radius:8px;
    color:var(--ink-2);padding:0 10px;font-size:var(--fs-hint);font-family:ui-monospace,'JetBrains Mono',monospace;letter-spacing:.04em}
  .hexinp:focus{border-color:var(--accent);outline:none;color:var(--ink)}
  .role-cols{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;padding:2px 0 4px}
  .role-c{display:flex;flex-direction:column;gap:4px;min-width:0}
  .role-c-hd{font-size:var(--fs-hint);color:var(--ink-2);text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .role-c .cr{width:100%;height:34px}
  /* Advanced sub-toggle inside Card spacing / Tail — collapses rarely-used rows */
  .rg{display:flex;align-items:center;gap:9px;flex:1;min-width:0}
  .rg input[type=range]{flex:1;min-width:30px;width:auto}
  .rg .val{font-size:var(--fs-hint);color:var(--ink-3);min-width:32px;text-align:right;font-variant-numeric:tabular-nums}
  /* editable number box on every slider (type a value) */
  .numval{width:46px;font-size:var(--fs-hint);text-align:center;border:1px solid var(--line-2);border-radius:6px;
    padding:3px 6px;background:var(--surface);color:var(--ink);font-variant-numeric:tabular-nums;
    -moz-appearance:textfield;appearance:textfield}
  .numval::-webkit-inner-spin-button,.numval::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none;margin:0}
  /* generated "all real controls" panel (#8 audit) — collapsible sections */
  .msec{padding:0;border:1px solid var(--line);border-radius:13px;background:var(--surface);margin-bottom:10px;overflow:hidden}
  .msec>summary{padding:14px 18px;font-size:var(--fs-section);font-weight:700;letter-spacing:.07em;text-transform:uppercase;
    color:var(--accent);cursor:pointer;list-style:none;display:flex;align-items:center;justify-content:space-between;
    transition:background .12s}
  .msec>.msec-hd{padding:14px 18px;font-size:var(--fs-section);font-weight:700;letter-spacing:.07em;text-transform:uppercase;
    color:var(--ink);display:flex;align-items:center}
  .msec>summary:hover{background:var(--accent-soft);animation:card-pop .18s ease-out}
  .msec>summary::-webkit-details-marker{display:none}
  .msec>summary::after{content:'⌄';font-size:var(--fs-section);line-height:1;color:var(--ink-3);transition:transform .18s;transform:rotate(-90deg)}
  .msec[open]>summary::after{transform:rotate(0deg)}
  .msec .body{padding:2px 18px 16px;background:var(--bg)}
  .msec .pr+.pr{border-top:1px solid var(--line)}
  .pr.na{opacity:.5}
  .sect-off{opacity:.2;pointer-events:none;filter:grayscale(.8)}
  .natag{font-size:var(--fs-micro);color:var(--ink-3);border:1px solid var(--line-2);border-radius:5px;padding:1px 5px;letter-spacing:.02em}
  .pop .x,.modal .x{position:absolute;top:9px;right:11px;border:none;background:none;color:var(--ink-3);font-size:var(--fs-label);cursor:pointer;line-height:1}
  .note{font-size:var(--fs-hint);color:var(--ink-3);padding:9px 18px;font-style:italic;flex:none}
  /* Export modal */
  .modal-bg{position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.45);display:none;align-items:center;justify-content:center;padding:20px}
  .modal-bg.show{display:flex}
  .modal{background:var(--surface);border:1px solid var(--line);border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,.3);
    padding:22px 24px;width:min(560px,100%);max-height:calc(100vh - 40px);overflow-y:auto;position:relative}
  .modal-sub{font-size:var(--fs-section);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--accent);margin:16px 0 6px}
  .steps{margin:0;padding-left:20px;font-size:var(--fs-body);color:var(--ink-2);line-height:1.75}
  .steps b{color:var(--ink)}
  .css-box{width:100%;height:120px;border:1px solid var(--line-2);border-radius:10px;background:var(--bg);color:var(--ink-2);
    font-family:ui-monospace,monospace;font-size:var(--fs-mono);padding:10px;resize:vertical;margin-top:4px}
  /* Export actions (owner 2026-07-04, "ให้ดูสมดุลกว่านี้"): ragged flex-wrap (3 buttons of uneven
     width, then 2 more leaving dead space at the end of row 2) read as lopsided. ONE primary action
     (Copy CSS — the actual OBS workflow) full-width on its own row, the 4 secondary actions below in
     an even 2×2 grid (same 1fr-1fr convention as .gallery/.lay-grid) — no ragged wrap, no dead cell. */
  .modal-actions{margin-top:14px}
  .modal-actions>.cta{width:100%;margin-bottom:8px}
  .modal-actions-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
  .modal-actions-grid .tb-btn{width:100%;justify-content:center}
  /* on-canvas direct manipulation: drag interior = move (axis-locked X/Y), drag edge = rotate */
  body.dragging,body.dragging *{user-select:none!important}
  body.mode-showcase .chat{user-select:none}
  .rail{position:fixed;z-index:78;display:none;pointer-events:none}
  .rail.x{border-top:2px dashed rgba(255,59,59,.7);width:320px;height:0}
  .rail.y{border-left:2px dashed rgba(34,211,106,.7);height:320px;width:0}
  .readout{position:fixed;z-index:82;display:none;background:#222;color:#fff;font-size:var(--fs-micro);font-weight:600;
    padding:3px 8px;border-radius:7px;transform:translate(-50%,-160%);pointer-events:none;white-space:nowrap}
  /* the "level box" — the renderer/ROW outline shown on selection (the editable extent) */
  /* the level box of ONE message (renderer) — dashed box around its avatar+chip+bubble */
  .levelbox{position:fixed;z-index:46;display:none;pointer-events:none;border-radius:10px;
    border:1.5px dashed var(--accent);background:transparent}   /* no fill tint — bubble color/border must read true while editing */
  .levelbox.show{display:block}
  /* label sits ON the top dashed line (its solid bg breaks the dashes, fieldset-legend style) */
  /* label sits ABOVE the top edge (clear of the corner handles, which straddle the border line) so it
     never gets covered by a handle square — the old top:0 overlap clipped it ("Avat▢"). */
  .lb-tag{position:absolute;top:0;left:0;transform:translateY(calc(-100% - 9px));font-size:var(--fs-micro);font-weight:600;letter-spacing:.02em;
    background:var(--accent);color:#fff;padding:1px 8px;border-radius:6px;white-space:nowrap}
  /* draggable gap handle — sits in the gap below the selected message; drag ↕ to set --gap (Row gap) */
  #gapHandle{position:fixed;z-index:47;display:none;transform:translate(-50%,-50%);cursor:ns-resize;pointer-events:auto;
    background:var(--accent);color:#fff;border-radius:20px;padding:2px 9px;font-size:var(--fs-micro);font-weight:600;white-space:nowrap;
    box-shadow:0 1px 4px rgba(0,0,0,.25);user-select:none;display:none;align-items:center;gap:5px}
  #gapHandle.show{display:inline-flex}
  #gapHandle b{font-variant-numeric:tabular-nums}
  #gapHandle span{opacity:.8;font-weight:500}
  /* draggable chat-width handle — red grip on the column-edge guide + label; drag ⇔ to set --bw (Chat width) */
  #widthHandle{position:fixed;z-index:47;display:none;transform:translateY(-50%);cursor:ew-resize;pointer-events:auto;
    align-items:center;gap:6px;white-space:nowrap;user-select:none}
  #widthHandle.show{display:inline-flex}
  #widthHandle.right{flex-direction:row-reverse;transform:translate(-100%,-50%)}
  #widthHandle .wh-bar{width:8px;height:46px;border-radius:6px;background:rgba(80,160,255,.95);box-shadow:0 1px 4px rgba(0,0,0,.35);margin:0 0 0 -4px}
  #widthHandle.right .wh-bar{margin:0 -4px 0 0}
  #widthHandle .wh-lbl{font-size:var(--fs-hint);font-weight:700;color:rgba(80,160,255,.8);
    background:rgba(80,160,255,.14);padding:4px 10px;border-radius:7px}
  /* avatar resize box (a second dashed outline around the avatar) */
  .avbox{border-color:var(--accent)}
  /* when the avatar is selected the message box is just a guide — fade it, hide its handles */
  .levelbox.guideonly{border-color:var(--line-2);background:transparent}
  .levelbox.guideonly .rh{display:none}
  /* the row label ("xxx Message box") shows even while editing the avatar (guideonly) — it gives the
     context "which message's avatar". (Was hidden; Q1 — the avatar box's own tag is hidden instead.) */
  /* Honest handles: avatar/deco resize is UNIFORM → corners only; the message/card box resizes WIDTH only
     (onRz uses the horizontal component for --bw, n/s do nothing) → side handles only. Hides the no-op ones. */
  .avbox .rh.n,.avbox .rh.s,.avbox .rh.e,.avbox .rh.w{display:none}
  #levelBox:not(.guideonly) .rh:not(.w):not(.e){display:none}
  /* 8 handles (corners + edge midpoints), Clip-Studio transform style — small white squares with an
     accent outline. The 16px hit box is bigger than the square for easy grabbing. */
  .levelbox .rh{position:absolute;width:16px;height:16px;margin:-8px;pointer-events:auto;display:flex;align-items:center;justify-content:center}
  .levelbox .rh::after{content:'';width:7px;height:7px;border-radius:1px;background:#fff;
    border:1.5px solid var(--accent);box-shadow:0 1px 2px rgba(0,0,0,.3);transition:transform .1s}
  .levelbox .rh:hover::after{transform:scale(1.4)}
  .levelbox .rh.nw{top:0;left:0;cursor:nwse-resize}
  .levelbox .rh.n {top:0;left:50%;cursor:ns-resize}
  .levelbox .rh.ne{top:0;left:100%;cursor:nesw-resize}
  .levelbox .rh.w {top:50%;left:0;cursor:ew-resize}
  .levelbox .rh.e {top:50%;left:100%;cursor:ew-resize}
  .levelbox .rh.sw{top:100%;left:0;cursor:nesw-resize}
  .levelbox .rh.s {top:100%;left:50%;cursor:ns-resize}
  .levelbox .rh.se{top:100%;left:100%;cursor:nwse-resize}
  /* ▶ Preview: the real feed (genCSS on real yt DOM) lives INSIDE #canvas, so it shares the
     exact 1920×1080 size + scale + backdrop as the Edit mock → Preview == Edit == OBS. */
  /* Bottom-anchored feed, replicating real OBS (CDP-verified). flex-direction:column-reverse pins
     the newest row to the bottom AND clips the OLDEST at the TOP when the stack overflows — so old
     messages slide UP and OFF the top edge (not vanish while still visible, the earlier bug). No
     scrollbar (OBS has none), no scroll math; the only animated motion is genCSS's per-row
     slideinup, and old rows snap up one row as new ones arrive (exactly like OBS). realpreview
     evicts a row only once it is fully above the top edge. display:flex toggled on by mode-live. */
  .realprev-body{position:absolute;inset:0;overflow:hidden;display:none;flex-direction:column-reverse;background:transparent;box-sizing:border-box;padding:40px}
  body.mode-live #realPreviewBody{display:flex}
  body.mode-live #chat{display:none}
  body.mode-live .guides{display:none}
  /* custom emote (real YT emotes are <img>) — sized by --emotesz; "Enlarge" bumps it (the s64 swap effect) */
  .emote{height:var(--emotesz,24px);width:auto;vertical-align:-0.25em;margin:0 1px}
  /* HD = crispness only (the s16→s64 swap, done in genCSS export). Size is set solely by Badge
     size / Emote size — the old hd-on 1.4×/1.7× bumps were removed so HD no longer changes size
     and Edit matches Preview/OBS (genCSS uses the size controls, never an HD multiplier). */

  /* Mobile: sidebar → bottom sheet; show Zone 1 only (style/layout/font) */
  @media(max-width:680px){
    .sidebar{position:fixed;top:auto;bottom:0;left:0;right:0;width:auto;height:44vh;
      border-radius:16px 16px 0 0;border-right:none;border-bottom:none;border-left:none;
      box-shadow:0 -4px 24px rgba(0,0,0,.18)}
    #wrapSec,#morePanel,#globalPanel details:nth-child(n+6){display:none}
    .strip{display:none}
  }
