/* Toolbar.
   Owned by src/toolbar.js.
   Loaded from index.html. See the ownership lanes in CLAUDE.md. */

/* A bar of tools floating at the foot of the canvas, centred on it.
   Stretched between the two panels' columns and then given a content-sized
   width inside that span, so `margin: auto` centres it on the same gap the
   artboard centres in — not on the window, which would push it off-centre by
   however much the two panels differ. It hugs its tools: a full-width bar
   would be mostly empty rule, and the canvas is what should be looked at. */
.toolbar {
  position: absolute;
  bottom: var(--panel-float-inset);
  left: calc(var(--panel-left) + var(--panel-float-inset));
  right: calc(var(--panel-right) + var(--panel-float-inset));
  z-index: 3; /* over the canvas layer (0) and clear of the panels (2) */
  width: max-content;
  max-width: 100%;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 2px;
  height: var(--toolbar-h);
  padding: 0 8px;
  background: var(--bg-panel);
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-panel);
}
/* The bar is filled by toolbar.js the moment the module graph runs — well
   before the deck loads — but the page can paint once before that, and an
   empty card there reads as something broken rather than something coming. */
.toolbar:empty { visibility: hidden; }

/* display: inline-block so its width/height apply regardless of what
   contains it — a flex container blockifies a direct child for free (the
   one between the mode switch and the insert tools rides on that), but one
   dropped inside .toolbar__inserts, a plain block div, would otherwise stay
   a bare inline <span> and collapse to zero width. */
.toolbar__divider {
  display: inline-block;
  width: 1px;
  height: 20px;
  margin: 0 8px;
  background: var(--line);
}

.tool {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 30px;
  padding: 0 10px;
  font: inherit;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-radius: var(--radius);
  cursor: var(--cursor-pointer);
  white-space: nowrap;
}
.tool:hover { background: var(--bg-hover); }
.tool:active { background: var(--bg-active); }
.tool:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
/* A commenter/viewer's own read-only pass — see toolbar.js's own
   canEditDeck() gate — dims an insert tool rather than hiding it, so the
   row still reads as "everything this app can put on a slide," just not
   reachable right now. */
.tool:disabled { color: var(--ink-soft); cursor: not-allowed; opacity: 0.45; }
.tool:disabled:hover { background: transparent; }
.tool:disabled .icon { color: var(--ink-soft); }
/* Defaults come from .icon in base.css; only the tint is toolbar-specific. */
.tool .icon { color: var(--ink-soft); }
.tool:hover .icon { color: var(--accent); }
.tool--icon { padding: 0 8px; }
.tool.is-on { background: var(--bg-accent-soft); color: var(--accent); }
.tool.is-on .icon { color: var(--accent); }

/* Rive's own mark is a filled shape, not a stroke like every Lucide glyph
   .icon is built for — fill takes over from currentColor's usual job on
   stroke, rather than vanishing under .icon's fill: none. */
.tool__rive { fill: currentColor; stroke: none; }

/* A split tool (Shapes, Data graphs, toolbar-shapes.js/toolbar-graphs.js):
   a plain .tool that draws whichever entry is current, plus its own small
   caret that opens the flyout of every entry. Two independently pressable
   buttons, not one — the caret's own hover/active never lights up the main
   glyph beside it, and the reverse, which a negative margin pulling them
   into each other would have undone: their two boxes would have overlapped,
   and whichever sat on top would have caught every hover meant for the
   other. A small positive gap instead — close enough to read as one
   control at rest, when neither is pressed and there is no fill to tell
   them apart by, but with a hairline of daylight always between their two
   boxes so a hover only ever lights up the one it actually landed on. */
/* The main half keeps .tool--icon's own plain padding, unmodified — the
   same size button as any tool with no flyout of its own (the pointer,
   comment, T, Rive), so a split tool's icon reads at the same weight as
   theirs rather than a cramped one of its own. */
.tool-split { display: inline-flex; align-items: center; gap: 1px; }

/* Smaller than the glyph it modifies, since it is a hint about that glyph
   and not a second, equally-weighted icon of its own. Centred in its own
   button rather than pushed to one side of it, the same as any other
   icon-only control. */
.tool-split__caret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink-soft);
  cursor: var(--cursor-pointer);
}
.tool-split__caret:hover { background: var(--bg-hover); color: var(--accent); }
.tool-split__caret:active { background: var(--bg-active); }
.tool-split__caret:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.tool-split__caret .icon { --icon-size: 11px; }
.tool-split__caret:disabled { cursor: not-allowed; opacity: 0.45; }
.tool-split__caret:disabled:hover { background: transparent; }

.palette__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  margin-top: 6px;
  padding: 6px;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  font: inherit;
  color: var(--ink);
  cursor: var(--cursor-pointer);
}
/* The gap between rows is each row's own margin-top, so the first row has
   none: the menu's 8px padding is then the whole gap above the first row,
   the same as below the last. */
.palette > .palette__row:first-child { margin-top: 0; }
.palette__row:hover { background: var(--bg-hover); }
.palette__row.is-on { color: var(--accent); font-weight: 600; }
/* A row whose feature isn't built yet (openTextMenu's own `disabled`) —
   present so its eventual position is never a surprise, dimmed so it doesn't
   read as a live choice in the meantime, `not-allowed` rather than the row's
   own pointer since there is nothing here a click would do. */
.palette__row:disabled { color: var(--ink-soft); cursor: not-allowed; }
.palette__row:disabled:hover { background: transparent; }
/* A row's optional glyph and its label, kept as one flex item so the row's
   own space-between (for a swatch or similar pinned to the far edge) cannot
   pull them apart. The icon takes the row's own colour — accent when is-on
   the same as the text — for free, since .icon strokes currentColor. */
.palette__row-label { display: flex; align-items: center; gap: 8px; }
/* A row's optional keyboard shortcut, pinned to the far edge by the row's
   own space-between — soft and small so it reads as a hint rather than
   competing with the label for attention. */
.palette__row-shortcut { font-size: 12px; color: var(--ink-soft); }
/* A plain toggle row's own mark (openTextMenu's own `checked`), pinned to
   the far edge the same way a shortcut is — accent, so the one thing that
   changes is the glyph itself, not the label beside it. */
.palette__row-check { display: flex; color: var(--accent); }
.palette__row-check .icon { --icon-size: 14px; }

/* A plain rule between two groups of rows (openTextMenu's own `divider`
   item) — border rather than background, so it reads as a hairline the
   same weight every other divider in this app (.home__bar, .paneltabs) is,
   not a filled block. Only a top margin, matching every row's own
   margin-top: the row right after this still supplies its usual 6px on its
   own, so the gap below the divider comes out the same as an ordinary
   row-to-row gap rather than doubling up. */
.palette__divider { margin: 6px 0 0; border: 0; border-top: 1px solid var(--line); }

/* The icon tool's own panel (toolbar-icons.js) — a .gradedit dialog, the
   same shell inspector-library.js's own asset library builds on, with the
   same body height, since this is the same kind of panel doing the same
   kind of job — just filtered to a different set of folders and drawing
   much smaller tiles. Its own width is half that dialog's, though: a 24px
   tile picker has no use for a full asset grid's own room. No
   .gradedit__head: like the asset library, there's no header row of its
   own — a sidebar's own title (when one is showing, see .iconlib__title)
   is the drag handle, and .iconlib__close floats over the grid's own
   corner in its place.
   .gradedit.iconlib-dialog, not .iconlib-dialog alone: toolbar.css loads
   before inspector.css (index.html), so a plain single-class selector here
   would lose the cascade to .gradedit's own width/padding — same
   specificity, later file wins. .assetlib-dialog gets away with a plain
   selector only because it's declared later in that same file, right after
   .gradedit itself; this can't rely on that, so it wins on specificity
   instead. */
.gradedit.iconlib-dialog {
  width: 450px;
  padding: 0 12px;
  overflow: hidden;
  transform-origin: bottom center;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.gradedit.iconlib-dialog.is-hidden { transform: scale(0.85); opacity: 0; }

.iconlib__body { display: flex; height: 420px; gap: 10px; cursor: var(--cursor-grab); }

/* Present only while there is more than one Icon Library folder to choose
   between — see render()'s own prepend/remove in toolbar-icons.js. Its
   absence, not a hidden state, is what lets .iconlib__gridpane's own
   flex: 1 actually reach the width this row would otherwise still reserve a
   gap for. */
.iconlib__sidebar {
  flex: 0 0 160px;
  overflow-y: auto;
  padding-right: 6px;
  border-right: 1px solid var(--line);
  cursor: var(--cursor-grab);
}
/* Stands in for the header row this dialog doesn't have, and doubles as
   its drag handle — see the pointerdown wired to it in toolbar-icons.js.
   Sticky rather than a sibling of the scrolling rows below it, so it reads
   as this pane's own fixed title bar instead of scrolling out of view with
   the folder list underneath it. Only ever present alongside the sidebar
   itself — with one Icon Library folder there's nothing here worth naming,
   so neither exists and the panel is dragged by its own bare background
   instead (dragBackground, toolbar-icons.js). */
.iconlib__title {
  position: sticky;
  top: 0;
  margin: 0;
  padding: 12px 6px 8px;
  background: var(--bg-panel);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  cursor: var(--cursor-grab);
  user-select: none;
}
.gradedit.is-moving .iconlib__title,
.gradedit.is-moving .iconlib__body,
.gradedit.is-moving .iconlib__sidebar,
.gradedit.is-moving .iconlib__gridpane,
.gradedit.is-moving .iconlib__grid {
  cursor: var(--cursor-grabbing);
}
.iconlib__row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 28px;
  padding: 0 6px;
  border: 0;
  border-radius: var(--radius);
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  background: transparent;
  text-align: left;
  cursor: var(--cursor-pointer);
}
.iconlib__row:hover { background: var(--bg-hover); }
.iconlib__row.is-active { background: var(--bg-hover); font-weight: 500; }
.iconlib__row .icon { --icon-size: 14px; color: var(--ink-soft); flex: none; }
.iconlib__row.is-active .icon { color: var(--accent); }
.iconlib__row-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A plain flex column so .iconlib__search can sit above .iconlib__grid
   without either scrolling the other — the same shape
   inspector-library.js's own gridPane comment describes for the identical
   reason. */
.iconlib__gridpane { flex: 1; min-width: 0; display: flex; flex-direction: column; cursor: var(--cursor-grab); }
/* No left margin: lines this up with the first tile's own left edge, both
   starting at .iconlib__grid's own edge with nothing of the pane's own
   padding between them. Narrowed to 60%, the same width
   inspector-library.js's own search field is, for the same reason: a
   search field this short doesn't need to be as wide as the row of tiles
   it filters. */
.iconlib__search { position: relative; width: 60%; margin: 12px 0 8px; }
.iconlib__search .icon {
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-soft);
  pointer-events: none;
}
/* Fully rounded rather than this app's usual --radius: a search field reads
   as a search field by its pill shape as much as by its icon. */
.iconlib__search-input { width: 100%; height: 28px; padding: 0 8px 0 30px; border-radius: 999px; }

/* A compact grid of small tiles (24x24, a plain hover highlight) — auto-fill
   rather than a fixed column count, so the row genuinely uses whatever width
   it's given: more tiles per row with the sidebar gone, fewer with it
   showing, instead of either state leaving the same fixed grid stranded in
   part of the available space. */
.iconlib__grid {
  flex: 1;
  align-content: flex-start;
  display: grid;
  grid-template-columns: repeat(auto-fill, 24px);
  gap: 2px;
  overflow-y: auto;
}
/* renderGrid's own empty/no-folders state (toolbar-icons.js) — a plain
   paragraph, which a still-active 24px grid would otherwise size as one of
   its own columns, wrapping the message to one word a line instead of
   letting it read as a sentence across the pane's own full width. */
.iconlib__grid--empty { display: block; }
/* The marker toolbar-icons.js's fillTiles watches to add the next batch —
   a full-row, 1px-high cell at the end of a run of tiles, so it never takes
   a tile's slot. */
.iconlib__more { grid-column: 1 / -1; height: 1px; }
/* Grouped mode — a selected folder that actually has sections, the same
   structure inspector-library.js's own .assetlib__grid--sections gives the
   asset library (renderSectioned/sectionBlock, toolbar-icons.js): the grid
   itself stops being tiles-in-a-row and becomes a stack of labelled
   .iconlib__section blocks instead, each with its own tile row below its
   own head. */
.iconlib__grid--sections {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* Pinned while its own section scrolls past — the identical
   position: sticky; top: 0 trick .assetlib__section-head already uses, for
   the identical reason: the next section's own head is what visually
   replaces this one, simply by being later in document order and stuck to
   the same edge. An opaque background is what keeps that handoff reading as
   one heading swapping for another instead of two overlapping while tiles
   scroll past beneath. */
.iconlib__section-head {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 4px 0;
  background: var(--bg-panel);
}
.iconlib__section-label { font-size: 11px; font-weight: 600; color: var(--ink); }
/* "Unsectioned" reads as the catch-all it is, not a named group the author
   chose — the same quieter, uppercase treatment dam.css's own version of
   this label, and .assetlib__section-label--muted, already get. */
.iconlib__section-label--muted {
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.iconlib__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, 24px);
  gap: 2px;
}
.iconlib__tile {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 3px;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  cursor: var(--cursor-pointer);
}
.iconlib__tile:hover { background: var(--bg-hover); }
/* width/height, not max-width/max-height: a source image smaller than the
   tile (many an actual icon file will be) has to scale *up* to fill it too,
   not just be capped from scaling past it — object-fit: contain is what
   still keeps it from stretching out of its own proportions either way. */
.iconlib__thumb { width: 100%; height: 100%; object-fit: contain; }
/* The icon files are drawn black, for the white slides they mostly land on,
   and they still insert that way — only the picker's own preview turns
   white in dark theme, where black glyphs vanish into the panel. Flattened
   to black first, so a glyph that isn't pure black still comes out white
   rather than as its own colour inverted. */
:root[data-theme="dark"] .iconlib__thumb { filter: brightness(0) invert(1); }
.iconlib__empty { margin: 12px 6px; color: var(--ink-soft); font-size: 13px; }
/* Floats at the window's own top-right corner of the dialog in place of the
   close button a header row would otherwise have carried — the same
   anchoring inspector-library.js's own .assetlib__close uses, for the same
   reason: there is no header here for it to sit in. */
.iconlib__close { position: absolute; top: 10px; right: 10px; z-index: 1; }
