/* neur0list-appearance-panel.css
   ------------------------------------------------------------
   Hand-written UI chrome for the Appearance modals (neur0list-
   appearance-panel.js) — the List-level "Appearance" Settings
   panel and the per-entry override panel. Deliberately kept
   separate from the GENERATED neur0list-appearance.css (which
   only ever holds the per-field element-styling rules produced
   by generate-appearance-css.js) so this file can be hand-edited
   freely without fighting the generator.

   Editor-only (loaded in NEUR0LIST_EDITOR, same as neur0list-
   meta-editor.css) — a plain viewer never opens these modals.
   Reuses the existing --nl-control-bg / --border-panel / --bg-card
   / --nl-content-color tokens so it matches the icon/color/meta
   pickers it's modeled on, rather than inventing a parallel look.
   ------------------------------------------------------------ */

/* ── Docked side drawer, not a centered full-screen modal ─────
   Same "barely-there backdrop, panel pinned to the right edge"
   pattern as .neur0list-color-picker-overlay (neur0ui-popup.css) —
   lets the list stay visible (and live-updating) behind the panel
   while a color/font/spacing choice is being made. Overrides just
   the positioning properties of the shared .neur0list-icon-picker-
   overlay base class (position/inset/z-index/display stay
   inherited from it — see neur0ui-popup.css). */
.neur0list-icon-picker-overlay.neur0-appearance-overlay {
  align-items: stretch;
  justify-content: flex-end;
  background: rgba(0, 0, 0, 0.08);
  backdrop-filter: none;
}

.appearance-panel {
  /* Narrower now that fields stack instead of sitting in a wide
     row (see .appearance-inline-row) — was 420px. Matches the
     color-picker panel's own 320px so the two read as a pair when
     they dock side by side (see the :has() rule near the bottom of
     this file). Keep both widths in sync if either changes. */
  width: min(320px, 92vw);
  max-height: 100vh;
  height: 100%;
  margin: 0;
  overflow-y: auto;
  border-radius: 1rem 0 0 1rem;
  box-shadow: -8px 0 32px rgba(0, 0, 0, 0.25);
}

/* Title + mode switch + preset/reset buttons, pinned to the top of
   the drawer while .appearance-body scrolls underneath — see the
   buildAppearanceOverlay comment in neur0list-appearance-panel.js.
   Needs its own opaque background since sticky content still sits
   above whatever scrolls past it. */
.appearance-sticky-head {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-card, #fff);
  padding-bottom: 0.4rem;
  margin: 0 -0.2rem;
  padding-left: 0.2rem;
  padding-right: 0.2rem;
  border-bottom: 1px solid var(--border-soft);
}

/* ── Edit colors for: Day/Dark ────────────────────────────────
   Every color field shows ONE swatch (see makeColorSwatch's
   callers) for whichever mode this select is set to, instead of a
   Day swatch and a Dark swatch side by side on every field — that
   was too much at once. Switching this rebuilds the field list
   (neur0RenderAppearanceGroups), so it's always reading/writing
   the right .color.day / .color.dark path. */
.appearance-mode-row {
  margin: 0.3rem 0 0.2rem;
}

.appearance-mode-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--nl-content-color);
}

.appearance-mode-select {
  font-size: 0.82rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.4rem;
  background: var(--bg-card, #fff);
  color: inherit;
  font-weight: 600;
}

.appearance-top-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.4rem 0 0.8rem;
}

.appearance-body {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.appearance-group {
  border: 1px solid var(--border-panel);
  border-radius: 0.8rem;
  padding: 0.5rem 0.8rem;
  background: var(--nl-control-bg);
}

/* Now a <button>, not a <div> — click to expand/collapse this
   section. Reset the native button chrome by hand (rather than
   `all: unset`) so nothing browser-default sneaks back in. */
.appearance-group-heading {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  width: 100%;
  border: none;
  background: transparent;
  padding: 0.2rem 0;
  margin: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--h2-text);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  opacity: 0.85;
}

.appearance-group-heading:hover {
  opacity: 1;
}

.appearance-group-caret {
  font-size: 0.7rem;
  width: 0.9rem;
  text-align: center;
  opacity: 0.75;
  text-transform: none;
}

.appearance-group-body {
  margin-top: 0.4rem;
}

.appearance-group-body[hidden] {
  display: none;
}

.appearance-field {
  padding: 0.55rem 0;
  border-top: 1px solid var(--border-soft);
}

.appearance-field:first-of-type {
  border-top: none;
  padding-top: 0;
}

.appearance-field-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.35rem;
}

.appearance-field-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--nl-content-color);
  flex: 1;
}

.appearance-field-reset {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 0.85rem;
  line-height: 1;
  padding: 0.2rem 0.3rem;
  border-radius: 0.4rem;
  opacity: 0.7;
}

.appearance-field-reset:hover {
  opacity: 1;
  background: var(--nl-control-bg-hover);
}

.appearance-field-note {
  font-size: 0.72rem;
  color: var(--nl-muted-text);
  margin-bottom: 0.4rem;
}

/* Stacked, not side-by-side: Type / Size / Color each get their
   own row instead of competing for width in one wrapping flex row.
   Was flex-wrap + align-items:flex-end, which crammed a font
   dropdown, a number input, and a color swatch into whatever width
   the panel had left — the panel had to stay wide (420px) just to
   fit that row without wrapping badly. Stacking means every
   control only ever needs its own width, so the panel itself can
   be narrower (see .appearance-panel below) — taller list of
   fields, much narrower drawer, matching what was asked for. */
.appearance-field-controls {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
}

/* ── Shared "Label: control" row ──────────────────────────────
   Type / Size / Color (and the various number fields — Corner
   size, Stroke size, Letter spacing, etc.) each render as one
   compact row: a short fixed-width label, then the control filling
   whatever's left. Replaces the old label-above-control stacks
   (which took 2-3 lines per control) — same total field count, far
   less vertical AND horizontal space per one, which is what let
   .appearance-panel shrink below. */
.appearance-inline-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  color: var(--nl-muted-text);
}

.appearance-inline-label {
  flex: 0 0 auto;
  min-width: 3.4rem;
  font-weight: 600;
}

.appearance-inline-label::after {
  content: ":";
}

/* ── Color swatch ─────────────────────────────────────────── */
.appearance-color-swatch-btn {
  width: 2.2rem;
  height: 1.6rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.4rem;
  cursor: pointer;
  padding: 0;
  flex: 0 0 auto;
}

.appearance-color-swatch-btn.is-transparent {
  background-image:
    linear-gradient(45deg, rgba(0,0,0,0.12) 25%, transparent 25%),
    linear-gradient(-45deg, rgba(0,0,0,0.12) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, rgba(0,0,0,0.12) 75%),
    linear-gradient(-45deg, transparent 75%, rgba(0,0,0,0.12) 75%);
  background-size: 8px 8px;
  background-position: 0 0, 0 4px, 4px -4px, -4px 0;
}

/* ── Number input ─────────────────────────────────────────── */
.appearance-number-input {
  width: 4.5rem;
  font-size: 0.8rem;
  padding: 0.25rem 0.35rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.4rem;
  background: var(--bg-card, #fff);
  color: inherit;
  flex: 0 0 auto;
}

.appearance-number-unit {
  font-size: 0.65rem;
  opacity: 0.7;
}

/* ── Font select ──────────────────────────────────────────── */
.appearance-font-select {
  font-size: 0.8rem;
  padding: 0.25rem 0.35rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.4rem;
  background: var(--bg-card, #fff);
  color: inherit;
  flex: 1 1 auto;
  min-width: 0; /* lets the select actually shrink to the row instead of forcing it wide */
}

/* ── Border field ─────────────────────────────────────────── */
.appearance-field-border {
  flex-direction: column;
  align-items: stretch;
}

.appearance-border-style-select {
  font-size: 0.8rem;
  padding: 0.25rem 0.35rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.4rem;
  background: var(--bg-card, #fff);
  color: inherit;
  align-self: flex-start;
}

.appearance-advanced-toggle {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.75rem;
  color: var(--nl-control-text);
  cursor: pointer;
}

.appearance-advanced-grid {
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: 0.5rem;
}

.appearance-advanced-grid[hidden] {
  display: none;
}

/* ── Live sample, right inside the field ─────────────────────
   See makePreviewBox's comment in the .js file for why this
   exists: a guaranteed-visible preview that doesn't depend on the
   real list staying visible behind the drawer. Checkerboard base
   (same pattern as .appearance-color-swatch-btn.is-transparent)
   so a transparent fill still reads as "transparent," not "white,"
   under a color/panel/background preview. */
.appearance-field-preview {
  margin-top: 0.15rem;
  border-radius: 0.5rem;
  background-image:
    linear-gradient(45deg, rgba(0,0,0,0.06) 25%, transparent 25%),
    linear-gradient(-45deg, rgba(0,0,0,0.06) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, rgba(0,0,0,0.06) 75%),
    linear-gradient(-45deg, transparent 75%, rgba(0,0,0,0.06) 75%);
  background-size: 10px 10px;
  background-position: 0 0, 0 5px, 5px -5px, -5px 0;
}

.appearance-field-preview-text {
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--border-soft);
  background-color: var(--bg-card, #fff);
  background-blend-mode: normal;
  line-height: 1.3;
}

.appearance-field-preview-panel {
  padding: 0.5rem 0.7rem;
  border-style: solid;
  border-width: 0;
  font-size: 0.75rem;
  color: var(--nl-muted-text);
  display: flex;
  align-items: center;
  min-height: 2rem;
}

.appearance-field-preview-bg {
  height: 1.8rem;
}

.appearance-field-preview-border {
  height: 2.6rem;
  border: 1px solid var(--border-panel);
  background-color: var(--bg-card, #fff);
}

/* ── Keep the shared color picker from overlapping this drawer ──
   Both .neur0-appearance-overlay and .neur0list-color-picker-overlay
   dock to the right edge (see this file's header comment) — fine on
   their own, but a color swatch inside the Appearance drawer opens
   the color picker ON TOP of the drawer, right where you'd want to
   see the field you're editing.

   First pass shifted the COLOR PICKER left instead — worked, but
   put the color picker (the thing you're actively using) on the
   left and the drawer on the right, which read wrong: the picker
   should stay put on the right, where it naturally docks, right
   next to the swatch you clicked. So instead: while a color picker
   is open ON TOP OF an open Appearance drawer, shift the DRAWER
   left by the picker's width (.color-picker-panel is 320px — see
   neur0ui-popup.css) and leave the color picker docked at its
   normal right-edge position. Pure CSS (:has()), reading both
   overlays' own [hidden] state directly rather than a JS class
   toggle that could drift out of sync with them. */
body:has(#neur0-appearance-list:not([hidden])):has(.neur0list-color-picker-overlay:not([hidden])) #neur0-appearance-list .appearance-panel,
body:has(#neur0-appearance-entry:not([hidden])):has(.neur0list-color-picker-overlay:not([hidden])) #neur0-appearance-entry .appearance-panel {
  margin-right: min(320px, 85vw);
}

/* Drop the color picker's own backdrop tint while it's nested over
   an open Appearance drawer: it's a full-viewport rgba(0,0,0,0.08)
   overlay (inset:0) same as this one, so even docked beside the
   drawer instead of on top of it, it was still dimming the *entire*
   screen a second time — including the sliver of live list preview
   the drawer leaves visible, which read as the color picker
   "covering" the only part of the page you could still see. One dim
   layer (the Appearance overlay's own) is enough; the color picker
   panel itself still has its own opaque background/shadow, so it
   stays perfectly legible without a backdrop of its own. */
body:has(#neur0-appearance-list:not([hidden])) .neur0list-color-picker-overlay,
body:has(#neur0-appearance-entry:not([hidden])) .neur0list-color-picker-overlay {
  background: transparent;
}

/* ============================================================
   Below: v2 drawer/tabs prototype styles (formerly
   neur0list-appearance-panel-v2.css), concatenated in after v1's
   base classes. v2 reuses several v1 rules directly
   (.appearance-inline-row, .appearance-number-input,
   .appearance-font-select, .appearance-advanced-grid, etc.)
   without redefining them, so this file depends on v1's rules
   staying loaded above. v1's now-inert overlay/panel selectors
   (#neur0-appearance-list/#neur0-appearance-entry,
   .appearance-panel, .appearance-group accordion styles) are
   left in place above — they simply stop matching once v2's
   differently-classed/id'd markup takes over below.
   ============================================================ */

/* neur0list-appearance-panel-v2.css
   ------------------------------------------------------------
   Styling for the v2 prototype shell (neur0list-appearance-
   panel-v2.js). neur0list-appearance-panel.css (v1) is untouched.

   One breakpoint (AV2_BREAKPOINT = 720px, matched in the two
   @media blocks below) decides the shape:

   - Above it: a docked drawer, same right-edge pattern v1 used,
     but now TWO columns side by side — preview | controls —
     instead of one narrow column. The preview column is what
     used to be free-floating space a second color-picker overlay
     needed to dock into; now it's a permanent, bounded part of
     the same drawer chrome.

   - At or below it: a bottom sheet. One rule covers both a phone
     and a narrowed desktop window on purpose — CSS can't tell
     them apart, and there's no reason the fix should either. The
     preview becomes a strip pinned above the scrolling controls
     instead of a side column.

   Reuses .icon-picker-panel / .icon-picker-title / .icon-picker-
   close and the .appearance-* field-control classes from
   neur0ui-popup.css and neur0list-appearance-panel.css so the
   controls themselves look identical to v1 — only the shell
   around them, and the color controls, are new.

   Also styles the tab bar + Color/Font/Extras kind select near the
   bottom of this file (.av2-tab-*). As of 2026-09-08 the tab SET
   itself is kind-dependent (see COLORS_TAB_AREAS_BY_SCOPE /
   TEXT_TAB_AREAS_BY_SCOPE in the .js file) — Colors kind shows
   Body/GUI/Data (3 buttons), Font/Extras show Title/Header/Body/
   Data or Header/Body/Data (up to 4). The tab row
   (.av2-tab-row-slot) stays pinned in the sticky header next to
   Close; the kind select (.av2-kind-select-slot) lives in the
   preview toolbar (.av2-preview-toolbar) above the preview mock
   instead, alongside the mode select and the top-action buttons —
   moved there so all three are reachable without scrolling on the
   bottom sheet.
   ------------------------------------------------------------ */

.av2-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  background: rgba(0, 0, 0, 0.08);
}

.av2-overlay[hidden] {
  display: none;
}

/* Extra specificity (two classes) so this reliably wins over
   .icon-picker-panel's own width/padding/radius/shadow regardless
   of stylesheet load order. */
.icon-picker-panel.av2-panel {
  width: auto;
  padding: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
  display: flex;
  overflow: hidden;
}

.av2-sheet-handle {
  display: none; /* shown only in the bottom-sheet layout below */
}

.av2-preview-col {
  display: flex;
  flex-direction: column;
  background: var(--nl-control-bg);
  overflow-y: auto;
}

/* Mode select + kind/preset select + top-action buttons, above the
   preview mock. Left-aligned, two explicit lines, per your sketch:
   "Editing: [ ] [Colors] [neur0loom - Day]" on its own line,
   "[Preset Colors] [Reset All]" on the next — not however-it-
   happens-to-wrap. Line 1 is modeRow + kindSelectSlot together
   (their natural widths, left to right); appearance-top-actions is
   forced onto its own line below via flex-basis:100% further down,
   regardless of how much width is left on line 1. On the bottom
   sheet this is the first thing after the drag handle, so it's
   reachable with zero scrolling either way. */
.av2-preview-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 0.3rem 0.5rem;
  padding: 0.5rem 0.9rem 0.15rem;
}

/* ☀️/🌒 toggle button, replacing the old Day/Dark <select> — per
   your note, keeping this as narrow as possible is what leaves
   room for "[ Colors ] for [ Theme ]" to stay on the same row. */
.appearance-mode-toggle {
  border: 1px solid var(--border-panel);
  border-radius: 0.5rem;
  background: var(--nl-control-bg);
  padding: 0.22rem 0.5rem;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

/* Kind select ("[ Colors ]") + "for" + the preset picker
   ("[ neur0loom - Day ]") travel together as one group now, so
   they wrap onto their own line as a unit instead of the "for" and
   preset select scattering to wherever .av2-preview-toolbar's flex-
   wrap happens to put them. */
.av2-kind-select-slot {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex-wrap: wrap;
}

.av2-kind-select-slot .av2-kind-select {
  margin: 0;
}

/* Width-capped with an ellipsis — per your correction, the OPTION
   TEXT stays the full preset name ("neur0loom - Day", and later
   whatever a custom preset is actually called — "Gotham City",
   "Sunset Skies"), the select just isn't allowed to stretch the row
   to fit it. A long name truncates with "…" instead of pushing
   "Editing: [ ]" onto its own line. */
.av2-preset-select {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.28rem 0.5rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.5rem;
  background: var(--nl-control-bg);
  color: var(--nl-control-text);
  max-width: 7.5rem;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Visible by default (desktop) — hidden on mobile in that media
   query below, where the width it costs is the difference between
   "Editing: [ ] [Colors] [Day]" fitting on one line or not. On the
   wide docked desktop drawer there's room to spare and it reads
   better with it. */
.av2-preset-for {
  font-size: 0.78rem;
  opacity: 0.65;
}

/* flex-basis:100% is the actual line-break — forces this group onto
   its own row below modeRow+kindSelectSlot no matter how much room
   is left on line 1, so the two-line shape holds even on a wide
   docked desktop panel, not just when mobile wrapping happens to
   land there. "Preview" sits on the left of this same row now
   (space-between), Use-preset/Reset-all on the right — one row
   holding both instead of "Preview" costing a row of its own. */
.av2-top-actions-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-basis: 100%;
  gap: 0.4rem;
}

.av2-preview-label {
  font-size: 0.78rem;
  font-weight: 600;
  opacity: 0.65;
  flex-shrink: 0;
}

/* margin: 0 here is a real override, not a no-op — "appearance-top-
   actions" is also a class name the live site uses elsewhere (its
   own default margin, e.g. ~0.4rem/0.8rem top/bottom, was leaking in
   here the same way .list-group's below does), and your own testing
   found removing it helped. Scoped under .av2-preview-toolbar so it
   only ever touches THIS toolbar's copy, never the live site's. */
.av2-preview-toolbar .appearance-top-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
}

.av2-preview-mock-slot {
  flex: 1;
  padding: 0.6rem 0.9rem 1.2rem;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

.av2-preview-mock {
  width: 100%;
  max-width: 22rem;
}

.av2-controls-col {
  display: flex;
  flex-direction: column;
  background: var(--bg-card, #fff);
  min-width: 0;
}

.av2-sticky-head {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-card, #fff);
  padding: 1rem 1rem 0.4rem;
  margin: 0;
  border-bottom: 1px solid var(--border-soft);
}

.av2-head-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.4rem;
}

/* Entry modal no longer sets a title (see the .js file's comment
   in neur0OpenEntryAppearanceModalV2) — collapse the row instead
   of leaving an empty label taking up space. List modal still sets
   "Appearance", so this only ever fires on the entry drawer. */
.icon-picker-title:empty {
  display: none;
}

/* Close lives up here now (see the .js file's shell comment) —
   always visible, never scrolled past. On the bottom sheet it
   floats above the sheet instead (see the mobile media query
   below) — this is its docked-drawer position. */
.av2-close {
  flex-shrink: 0;
}

.av2-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.8rem 1rem 1.2rem;
}

/* ── Docked drawer (desktop / wide window) ─────────────────── */
@media (min-width: 721px) {
  .av2-overlay {
    align-items: stretch;
    justify-content: flex-end;
  }

  .icon-picker-panel.av2-panel {
    flex-direction: row;
    /* 760px → 460px: was preview col (min 16rem) + controls col
       (320px) side by side; the preview is hidden on desktop now
       (.av2-preview-col below) and its toolbar reparents into the
       controls column instead (see the .js file's av2PlaceToolbar),
       so the panel only ever needs to fit that one column — your
       call, over keeping the old width and leaving empty space where
       the preview used to sit. */
    width: min(460px, 96vw);
    max-height: 100vh;
    height: 100%;
    margin: 0;
    border-radius: 1rem 0 0 1rem;
    box-shadow: -8px 0 32px rgba(0, 0, 0, 0.25);
    background: var(--bg-card, #fff);
  }

  /* Preview mock removed from the desktop view — browser only,
     definitely not on mobile, your words. Still rendered (nothing
     downstream changed; the mock-building code doesn't know or care),
     just not shown here: display:none rather than deleting the
     column, so nothing has to change for mobile, which still shows it
     as a strip above the scrolling controls (its own @media block
     below, untouched). previewToolbar — Editing/Colors/preset/
     reset — is the one piece of this column desktop still uses; the
     .js file pulls that one node out into .av2-controls-col before
     this rule hides what's left of the column around it. */
  .av2-preview-col {
    display: none;
  }

  /* Was a fixed 320px next to the (now-hidden) preview column; with
     the preview gone this is the only column left, so it fills the
     whole (now-narrower) panel width instead of staying pinned to a
     measurement sized for when it had a neighbor. */
  .av2-controls-col {
    flex: 1 1 auto;
    width: 100%;
  }

  /* ── Desktop-only: the relocated Editing/Colors/preset/reset
     toolbar, once it's actually sitting in .av2-controls-col ────
     Targeted by ANCESTOR (.av2-controls-col .av2-preview-toolbar,
     a descendant combinator — deliberately NOT ">", a direct-child
     combinator) rather than a modifier class the .js would have to
     remember to toggle: which of these rules applies is decided by
     which top-level column the node is actually under at that
     instant, so it can never drift out of sync with where
     av2PlaceToolbar actually put it. It has to be a plain descendant
     selector and not ">" because the toolbar doesn't land straight
     inside .av2-controls-col — it lands inside .av2-sticky-head,
     itself a child of controlsCol (see av2PlaceToolbar in the .js
     file: stickyHead.insertBefore(previewToolbar, tabRowSlot)); a ">"
     here would silently never match anything, one selector-parent
     level too strict. Caught that live in the headless-browser
     verification pass (verify3.js), not by inspection — the
     "Preview" label was still rendering on desktop with the first,
     over-strict version of this rule. Mobile's own copy of this same
     node, still under .av2-preview-col there, keeps matching the
     base/mobile rules earlier in this file completely undisturbed by
     anything below. Specificity (0,2,0) beats the plain
     .av2-preview-toolbar rule just below (0,1,0) regardless of
     source order, so these don't have to out-cascade that one by
     position in the file either. */
  .av2-controls-col .av2-preview-toolbar {
    padding: 0 0 0.6rem;
    margin-top: 0.2rem;
    border-bottom: 1px solid var(--border-soft);
    justify-content: flex-start;
    row-gap: 0.4rem;
  }

  /* "Preview" was the label for the mock right below it — with the
     mock hidden on desktop there's nothing left to label. Reset
     Colors / Reset All / Use-preset (the buttons sharing this same
     row, .appearance-top-actions inside topActionsRow) stay — those
     are three of the five things you asked to move up here. */
  .av2-controls-col .av2-preview-toolbar .av2-preview-label {
    display: none;
  }

  /* This row (Preview label + the reset/use-preset buttons) used to
     be forced onto its own line unconditionally (flex-basis: 100%,
     base rule) because the mock below it needed the row above kept
     predictable and narrow. Up here, with "more than enough room"
     (your words) and no mock to protect the layout of, let it share
     line 1 with Editing/Colors when it fits and wrap on its own
     otherwise, same as everything else in this toolbar now does. */
  .av2-controls-col .av2-preview-toolbar .av2-top-actions-row {
    flex-basis: auto;
    justify-content: flex-start;
  }

  /* "Editing: [ ]" stays on the left, "[Colors] for [theme]" pushes
     to the right of that same row — your ask. Mobile keeps its own
     flex-start (base rule) since everything there has to bunch up
     left just to fit; the wide docked drawer has room to spread out.
     topActionsRow (Preview + Use-preset/Reset-all) is unaffected —
     it's a separate full-width row (flex-basis:100%) either way, and
     already handles its own left/right split internally.

     Note this rule (unscoped to a parent) still also matches the
     relocated copy in .av2-controls-col at this same width — the
     compound rule above it wins on justify-content specifically
     (0,2,0 beats 0,1,0), so the relocated toolbar ends up flex-start
     as intended rather than this space-between; everything else this
     rule doesn't touch is inherited from here same as always. */
  .av2-preview-toolbar {
    justify-content: space-between;
  }

  /* 2026-09-08: the GUI-tab-pulled-out-to-the-right rule that used
     to live here (.av2-tab-btn.av2-tab-btn-gui { position: absolute;
     ... } plus .av2-tab-row's padding-right reservation) is REMOVED.
     It existed only to stop a 5-button row (Title/Header/Body/Data/
     GUI) from wrapping to a second line on the 320px-wide docked
     drawer. As of this date, GUI never shares a tab bar with Title/
     Header any more — Colors kind shows Body/GUI/Data (3 buttons)
     and Font/Extras show Title/Header/Body/Data (4, GUI absent) —
     see COLORS_TAB_AREAS_BY_SCOPE/TEXT_TAB_AREAS_BY_SCOPE in the .js
     file. Neither tab set wraps at this width even in natural flex
     order, and pulling GUI out to the far right of a 3-button row
     would misplace it (your reference screenshot shows Body, GUI,
     Data left-to-right, not Body/Data with GUI stranded on the
     right) — so this is a straight removal, not a replacement. */
}

/* ── Bottom sheet (phone, or a narrowed desktop window — same
   rule covers both) ───────────────────────────────────────── */
@media (max-width: 720px) {
  .av2-overlay {
    align-items: flex-end;
    justify-content: center;
  }

  /* Fixed height instead of auto — the old auto-height sheet grew
     and shrank with whatever tab/kind combo was showing, so the
     whole sheet visibly resized every time you tapped a tab. Grown
     from 87.5vh toward full-screen (your call, over shrinking the
     preview or relocating the toolbar) — the preview needed more
     guaranteed room than 34vh of 87.5vh was ever going to give it.
     Still leaves a sliver of scrim up top so tap-outside-to-close
     keeps working, and the fixed Close button (always pinned to the
     true viewport top, independent of the sheet) never overlaps it. */
  .icon-picker-panel.av2-panel {
    position: relative;
    flex-direction: column;
    width: 100%;
    max-width: 100%;
    height: 96vh;
    max-height: 96vh;
    margin: 0;
    border-radius: 1rem 1rem 0 0;
    box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.25);
    background: var(--bg-card, #fff);
  }

  .av2-sheet-handle {
    display: block;
    flex: 0 0 auto;
    width: 2.5rem;
    height: 0.3rem;
    border-radius: 999px;
    background: var(--border-panel);
    margin: 0.35rem auto 0.1rem;
  }

  /* Close is pinned to the true top of the SCREEN (position: fixed,
     not just above the sheet) — on a tall phone the scrim above an
     87.5vh sheet is a lot more than the ~44px this used to float up,
     so anchoring to the sheet's own top edge left it stranded in
     empty space, easy to miss. Fixed-to-viewport means it's always
     exactly where you'd look first. Tapping the dimmed scrim
     anywhere still closes it too (existing overlay-click handler),
     so there are two easy ways out. */
  .av2-close {
    position: fixed;
    top: max(0.75rem, env(safe-area-inset-top, 0px));
    right: 0.9rem;
    z-index: 10000;
    border-radius: 999px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
    background: var(--bg-card, #fff);
  }

  /* You called these two out by class name — the desktop values
     (1rem top padding on the sticky head, 0.8rem on the body) were
     never actually overridden for mobile until now, which is real
     dead space: Close floats out of flow up here (fixed, above), and
     the title is empty (see .icon-picker-title:empty), so
     .av2-head-top has nothing left in it worth padding around. This
     is most of "the gap between the Header [tab row] and Preview
     panel" — shrinking it here should close most of that visible
     gap between the bottom of the preview mock and the Header/Body/
     Data/GUI tab row. */
  .av2-sticky-head {
    padding: 0.2rem 0.75rem 0.15rem;
  }

  .av2-head-top {
    margin-bottom: 0;
    min-height: 0;
  }

  .av2-body {
    padding: 0.3rem 0.75rem 1rem;
  }

  /* Non-scrolling now, per your call — this used to share ONE
     scrollable region with the toolbar above it (.av2-preview-col's
     base overflow-y:auto, higher up in this file), so scrolling
     down to see the rest of the mock scrolled the Editing/Colors-
     for/Use-preset row away with it, off the top, behind the page
     banner. overflow:hidden here means that can't happen anymore —
     whatever doesn't fit gets clipped at the bottom instead of
     scrolling, which is what "its own non-scrolling area" means in
     practice. Bumped again to 46vh — the "for" label is gone, the
     "Appearance" title is gone, and the handle/toolbar padding is
     tighter, so the preview gets most of what that freed up (your
     "must show" priority). Paired with the tightened .av2-preview-
     mock spacing below — between the two there should rarely be
     anything left to clip, but flagging the clip-not-scroll tradeoff
     explicitly: if a mock still doesn't fit some phone/field
     combination, the bottom of the entry card is what disappears,
     silently. */
  .av2-preview-col {
    flex: 0 0 auto;
    max-height: 48vh;
    overflow: hidden;
    border-bottom: 1px solid var(--border-panel);
  }

  /* Scoped tightening of the mock's OWN internal spacing — these
     classes are the real .list-group-header/.list-card/etc., reused
     as-is from the live site (see buildPreviewMock's header comment)
     so this only touches them INSIDE .av2-preview-mock, never the
     real list on the page. Values are a first pass, not measured
     against your actual site CSS (that lives in files I don't have
     loaded locally) — nudge these further once you see it live. */
  /* .list-group itself (the outer wrapper, one level up from
     .list-group-header below) carries its own default margin on the
     live site — your own testing found trimming it to 0.4rem 0
     helped: with the preview mock's overflow:hidden + fixed max-
     height, every bit of margin here was cutting into how much of
     the card (down to the label/value row and meta tags) actually
     fit before getting clipped. */
  .av2-preview-mock .list-group {
    margin: 0.4rem 0;
  }

  .av2-preview-mock .list-group-header {
    padding-top: 0.4rem;
    padding-bottom: 0.4rem;
    margin-bottom: 0.3rem;
  }

  .av2-preview-mock .list-group-header h2 {
    margin: 0;
    line-height: 1.15;
  }

  .av2-preview-mock .group-subtitle {
    margin: 0.1rem 0 0;
  }

  .av2-preview-mock .list-card {
    margin: 0;
  }

  .av2-preview-mock .list-header {
    padding-top: 0.35rem;
    padding-bottom: 0.35rem;
  }

  .av2-preview-mock .item-title {
    margin: 0;
    line-height: 1.2;
  }

  .av2-preview-mock .list-meta {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
  }

  .av2-preview-mock .list-details {
    padding-top: 0.3rem;
    padding-bottom: 0.3rem;
  }

  .av2-preview-mock .details-label {
    margin-bottom: 0.1rem;
  }

  .av2-preview-mock .list-details p {
    margin: 0;
  }

  /* Tighter than desktop everywhere — you flagged this specifically:
     the top rows (Editing toggle, Colors/preset, Use preset/Reset
     all) can't scroll on mobile the way the preview mock below them
     can, so every bit of padding trimmed here is padding you don't
     have to scroll past just to see the preview at all. Pushed
     further this round ("move the top text and button up some with
     less padding") — top padding is nearly gone now, relying on the
     sheet handle's own small margin above it for breathing room
     instead of stacking two paddings on top of each other. */
  .av2-preview-toolbar {
    gap: 0.12rem 0.3rem;
    padding: 0.1rem 0.75rem 0;
  }

  /* Everything below exists for one reason: "Editing: [ ]" and
     "[Colors] [neur0loom - Day]" MUST land on the same line, not
     wrap — every value here is shrunk specifically to buy back the
     width that takes. Removing "for" (in the .js file) was the
     biggest single chunk; the preset select's own max-width +
     ellipsis (base rule above) does the rest of the job now — the
     option text stays the full preset name, but the select itself
     can't grow past a fixed width to show it, so a long name clips
     with "…" instead of wrapping the row. Shrunk a bit further here
     specifically for the smallest phones. */
  .appearance-mode-row {
    display: flex;
    align-items: center;
  }

  .appearance-mode-label {
    font-size: 0.78rem;
    display: flex;
    align-items: center;
    gap: 0.25rem;
  }

  .appearance-mode-toggle {
    padding: 0.3rem 0.3rem;
    font-size: 0.85rem;
  }

  .av2-kind-select-slot {
    gap: 0.25rem;
  }

  /* Mobile-only — this is the one your phone testing was actually
     about. Desktop gets "for" back (base rule, above); the phone-
     width sheet still can't spare the width. */
  .av2-preset-for {
    display: none;
  }

  .av2-kind-select-slot .av2-kind-select,
  .av2-preset-select {
    font-size: 0.7rem;
  }

  .av2-kind-select-slot .av2-kind-select {
    padding: 0.3rem 0.3rem;
  }

  .av2-preset-select {
    padding: 0.16rem 0.3rem;
    max-width: 8.5rem;
  }

  .av2-preview-toolbar .appearance-top-actions button {
    font-size: 0.76rem;
    padding-top: 0.22rem;
    padding-bottom: 0.22rem;
  }

  /* "Can we move the preview closer to the Preset buttons?" — zero
     top padding here plus zero bottom padding on .av2-preview-toolbar
     above means the preview mock sits right against the Use-preset/
     Reset-all row, no dead space between them. */
  .av2-preview-mock-slot {
    padding: 0 0.9rem 0.4rem;
  }

  /* The preview mock STAYS VISIBLE while a color's being edited now
     — your correction: hiding it just left a big empty gap at the
     bottom of the screen below the color box (since the box rendered
     right after Use-preset/Reset-all, at the TOP, with nothing below
     it to fill the rest of the fixed-height sheet). What you actually
     wanted was the color box pushed DOWN, past the preview, not the
     preview removed. So: the Text/Background/Stroke row and the
     Header/Body/Data/GUI tab row still hide while editing (that part
     you confirmed was good — "the colors don't show... which is
     good"), but the preview mock above them does not. The color box
     ends up rendering right after the (now-empty) tab/grid area, at
     the bottom of what's visible, below the still-showing preview —
     reachable by scrolling .av2-body a little if the preview's tall,
     same as any other field's content always has been. */
  .av2-panel.av2-color-editing .av2-tab-row-slot {
    display: none;
  }

  .av2-panel.av2-color-editing .av2-sticky-head {
    padding-bottom: 0;
    border-bottom: none;
  }

  .av2-panel.av2-color-editing .av2-color-grid-labelsrow,
  .av2-panel.av2-color-editing .av2-color-grid-swatchrow {
    display: none;
  }

  .av2-panel.av2-color-editing .av2-body {
    padding-top: 0.15rem;
  }

  .av2-controls-col {
    flex: 1 1 auto;
    min-height: 0;
  }

  /* "Bottom area" (the expanded swatch panel) a little smaller, per
     your note — smaller swatches, tighter gaps, less padding around
     the whole inline panel. Same 4 rows (grayscale + 3 hue rows),
     just each one shorter, so less of it needs scrolling into view
     inside .av2-body to reach the Done button. */
  .av2-color-inline {
    margin: 0.3rem 0 0.4rem;
    padding: 0.45rem;
  }

  .av2-color-swatchgrid {
    gap: 0.15rem;
  }

  .av2-color-swatchrow {
    gap: 2px;
  }

  .av2-color-swatch {
    max-width: 1.7rem;
  }
}

/* ── Inline accordion color controls ───────────────────────────
   Replaces the standalone .neur0list-color-picker-overlay for
   swatches opened from inside this panel — see the .js file's
   header comment for why (no second overlay, ever). */
.av2-color-field.is-open .appearance-color-swatch-btn {
  outline: 2px solid var(--nl-focus-ring);
  outline-offset: 1px;
}

/* Compact color field (fieldSubcontrol's text-kind Colors view,
   and every swatch inside the Colors-grid below) — no "Color:"
   label row, just the bare swatch button (plus its inline picker
   panel, invisible until opened — mounted here directly, or into
   a shared .av2-color-grid-inline-slot elsewhere in the grid, per
   opts.mountEl in makeColorSwatchV2). */
.av2-color-field-compact {
  margin: 0;
}

.av2-color-inline {
  margin: 0.4rem 0 0.6rem;
  padding: 0.6rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.6rem;
  background: var(--bg-card, #fff);
}

/* Only rendered for grid (compact) swatches — see openInline's own
   comment in the .js file. Stands in for the Text/Background/Stroke
   label that's hidden elsewhere on mobile while this is open, so
   which field you're picking a color for isn't lost. */
.av2-color-inline-label {
  font-size: 0.78rem;
  font-weight: 600;
  opacity: 0.75;
  margin-bottom: 0.35rem;
}

/* Swatches / Wheel sub-tabs, inside one field's expanding color
   panel — the wheel is back, scoped down and mounted inline (see
   buildInlineColorWheel's own comment in the .js file). */
.av2-color-subtabs {
  display: flex;
  gap: 0.3rem;
  margin-bottom: 0.5rem;
}

.av2-color-subtab {
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.22rem 0.6rem;
  border: 1px solid var(--border-panel);
  border-radius: 999px;
  background: transparent;
  color: var(--nl-content-color, #222);
  cursor: pointer;
}

.av2-color-subtab.is-active {
  background: var(--nl-control-bg-active, #c3d8ff);
  border-color: transparent;
}

/* Explicit close for the inline panel, per your note — pill-shaped
   to match the Swatches/Wheel/Slider tabs it sits beside, but a
   solid fill (not the same transparent look) and pushed to the far
   right so it reads as a distinct action, not a 4th tab. */
.av2-color-done-btn {
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.22rem 0.6rem;
  margin-left: auto;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--nl-control-bg-active, #c3d8ff);
  color: var(--nl-content-color, #222);
  cursor: pointer;
}

/* ── Wheel sub-tab ───────────────────────────────────────────
   Same visual recipe as the standalone picker's Wheel tab
   (neur0ui-popup.css's .color-picker-wheel/-marker), reused here
   under av2-* names so this stays independent of that file. */
.av2-wheel-pane {
  display: flex;
  gap: 0.7rem;
  align-items: center;
  flex-wrap: wrap;
}

.av2-wheel-wrap {
  flex-shrink: 0;
}

.av2-color-wheel {
  position: relative;
  width: 6.5rem;
  height: 6.5rem;
  border-radius: 50%;
  cursor: crosshair;
  touch-action: none;
  background-image:
    radial-gradient(circle, #fff 0%, transparent 70%),
    conic-gradient(from 90deg, red, yellow, lime, cyan, blue, magenta, red);
}

.av2-color-wheel-marker {
  position: absolute;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.av2-wheel-sliders {
  flex: 1;
  min-width: 8rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.av2-wheel-channel-row {
  display: grid;
  grid-template-columns: 1.1rem 1fr 2.2rem;
  align-items: center;
  gap: 0.4rem;
}

.av2-wheel-channel-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--nl-muted-text);
}

.av2-wheel-channel-value {
  font-size: 0.68rem;
  color: var(--nl-muted-text);
  text-align: right;
}

.av2-wheel-channel-slider {
  width: 100%;
}

/* ── Slider sub-tab (SV box + hue strip) ───────────────────────
   Same visual recipe as the standalone picker's Slider tab
   (neur0ui-popup.css's .color-picker-sv-box/-hue-strip), reused
   here under av2-* names so this stays independent of that file. */
.av2-slider-pane {
  display: flex;
  flex-direction: column;
}

.av2-slider-sv-box {
  position: relative;
  width: 100%;
  height: 6.5rem;
  border-radius: 0.5rem;
  border: 1px solid var(--border-panel);
  cursor: crosshair;
  touch-action: none;
  --sv-hue: 45;
  background-color: hsl(var(--sv-hue), 100%, 50%);
  background-image:
    linear-gradient(to top, #000, transparent),
    linear-gradient(to right, #fff, transparent);
}

.av2-slider-hue-strip {
  position: relative;
  width: 100%;
  height: 1rem;
  margin-top: 0.6rem;
  border-radius: 999px;
  cursor: pointer;
  touch-action: none;
  background: linear-gradient(to right,
    #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);
}

.av2-slider-sv-marker,
.av2-slider-hue-marker {
  position: absolute;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.av2-slider-hue-marker {
  top: 50%;
}

.av2-color-swatchgrid {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-bottom: 0.5rem;
}

.av2-color-swatchrow {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 3px;
}

.av2-color-swatch {
  width: 100%;
  aspect-ratio: 1;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 3px;
  cursor: pointer;
  padding: 0;
}

.av2-color-hexrow {
  margin-top: 0.3rem;
}

.av2-color-hex-input {
  flex: 1;
  font-size: 0.8rem;
  padding: 0.25rem 0.4rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.4rem;
  background: var(--bg-card, #fff);
  color: inherit;
  min-width: 0;
}

/* ── Border field's advanced (per-corner) grid ──────────────────
   Overrides the base .appearance-advanced-grid (neur0list-
   appearance-panel.css: display:grid; grid-template-columns:
   repeat(2, auto)) — that rule was built for 4 whole ctl.el rows
   as grid children (each row its own label+input+unit flex line),
   which is why the tl/tr/bl/br columns never actually lined up.
   renderBorderField's makeCornerInput instead hands back a bare
   label + input as two SEPARATE grid children per corner (label,
   input, label, input — two corners per row), so a real 4-column
   template with fixed track widths is what makes the number
   columns align — per your "try to get those columns to align
   better" note. Same fix covers entries.border (Header/Body) and
   metadata.border ("Chip border", Data) since both share this one
   field renderer. */
.icon-picker-panel.av2-panel .appearance-advanced-grid {
  display: grid;
  grid-template-columns: max-content 3.6rem max-content 3.6rem;
  align-items: center;
  column-gap: 0.5rem;
  row-gap: 0.4rem;
}

.appearance-advanced-corner-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--nl-muted-text);
}

.appearance-advanced-corner-input {
  width: 100%;
  font-size: 0.8rem;
  padding: 0.25rem 0.35rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.4rem;
  background: var(--bg-card, #fff);
  color: inherit;
}

.appearance-advanced-corner-input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Premium lock on the Advanced (per corner) toggle — see
   renderBorderField's own comment for which feature-tier flag
   this reads. */
.appearance-advanced-toggle.is-locked {
  opacity: 0.65;
  cursor: not-allowed;
}

.appearance-advanced-toggle.is-locked input[type="checkbox"] {
  cursor: not-allowed;
}

.appearance-advanced-lock {
  font-size: 0.75rem;
}

/* ── Colors-grid (Body/Data, Colors kind only) ───────────────────
   Per your "let's try columns" note — see renderColorsGrid's own
   comment in the .js file. Two grid rows (labels, then swatches)
   share the same column count so everything lines up; a shared
   .av2-color-grid-inline-slot underneath is where whichever swatch
   is currently open expands (full width, not squeezed into one
   narrow column — see makeColorSwatchV2's opts.mountEl). As of
   2026-09-08 Title/Header no longer have their own grids here —
   Colors kind only ever shows Body and Data — see
   COLOR_GRID_BY_SCOPE's own comment in the .js file. */
.av2-color-grid {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* Was display:grid; grid-template-columns set inline per-row from
   config.length (repeat(N, 1fr)) — that stretched every column to
   fill the FULL row width no matter how many there were: one
   column (e.g. Body's lone "Background") ended up dead-center of
   the whole panel, and four columns (Data's Label/Value/Background/
   Chip color) spread edge-to-edge with big gaps instead of sitting
   together. Per your "push them to the top-left, Windows-folder-
   style" note: flex-wrap with a fixed width per column instead —
   columns now pack from the left, wrap to a new row only when the
   panel's too narrow for the next one, and leave any leftover width
   empty on the right rather than stretching into it. The fixed
   width (not flex:1) is also what keeps the labels row and the
   swatches row lined up column-for-column, since they're two
   separate flex containers walking the same config list in the
   same order. */
.av2-color-grid-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 0.4rem;
}

/* 4.4rem + 0.6rem gaps (2026-09-08's original values) measured out
   to ~310px for 4 columns — comfortably fits a normal phone width,
   but not the 324px viewport you tested at (usable width there is
   ~300px after .av2-body's own side padding), so the 4th column
   ("Back"/Body Back) wrapped to its own row even with the shortened
   labels from earlier today — shortening a label doesn't change a
   FIXED flex-basis column's width, only whether its own text wraps
   inside that fixed box. Trimmed to 4rem columns + 0.4rem gaps
   (~280px for 4 columns) so the full row fits with room to spare
   even at 324px; a normal 375px+ phone was never the tight case. If
   a still-narrower device turns up, this is the number to revisit
   again, not the labels. */
.av2-color-grid-col-label,
.av2-color-grid-col {
  flex: 0 0 4rem;
}

.av2-color-grid-col-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  font-size: 0.72rem;
  font-weight: 600;
  text-align: center;
  color: var(--nl-content-color, #222);
}

.av2-color-grid-col-reset {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 0.7rem;
  opacity: 0.6;
  padding: 0.1rem;
}

.av2-color-grid-col-reset:hover {
  opacity: 1;
}

.av2-color-grid-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
}

.av2-color-grid-swatch-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
}

.av2-color-grid-part-label {
  font-size: 0.62rem;
  opacity: 0.65;
}

.av2-color-grid-inline-slot:empty {
  display: none;
}

/* ── Tab bar — Body/GUI/Data (Colors) or Title/Header/Body/Data
   (Font/Extras) ──────────────────────────────────────────────
   Just the tab buttons — the Color/Font/Extras kind select that
   used to sit next to them lives in .av2-preview-toolbar (above).
   The row lives in the sticky header (.av2-tab-row-slot, an empty
   slot buildAppearanceOverlayV2 leaves for it) so it's always
   reachable, same reasoning as Close.

   Squared-off bottom corners + pressed flush against the field
   list below, so these read as actual folder tabs sitting on top
   of the picking area rather than floating pill buttons — per your
   note. The sticky header's own bottom border is switched off
   when tabs are present (:has) since the tab row draws its own
   baseline instead, and the active tab's bottom border matches the
   body background so it visually "cuts into" that baseline —
   standard tab-widget trick for showing which one is open. */
.av2-tab-row-slot {
  margin-top: 0.2rem;
  border-bottom: 1px solid var(--border-panel);
}

.av2-tab-row {
  display: flex;
  gap: 0.15rem;
  flex-wrap: wrap;
}

.av2-tab-btn {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--border-panel);
  border-bottom: none;
  border-radius: 0.55rem 0.55rem 0 0;
  background: var(--nl-control-bg, transparent);
  color: var(--nl-content-color, #222);
  cursor: pointer;
  white-space: nowrap;
  position: relative;
}

.av2-tab-btn.is-active {
  background: var(--bg-card, #fff);
  border-color: var(--border-panel);
  margin-bottom: -1px;
  z-index: 1;
}

.av2-kind-select {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.28rem 0.5rem;
  border: 1px solid var(--border-panel);
  border-radius: 0.5rem;
  background: var(--nl-control-bg);
  color: var(--nl-control-text);
  flex: none;
}

.av2-tab-empty {
  font-size: 0.82rem;
  opacity: 0.65;
  padding: 1.5rem 0.2rem;
  text-align: center;
}