.el-toolbar {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  width: 100%;
  max-width: 380px;
}

/* Action row --------------------------------------------------------------- */

.el-toolbar__row {
  display: flex;
  align-items: center;
}

.el-toolbar__actions {
  gap: 0.4rem;
  flex-wrap: wrap;
  /* Left-justified, not space-between. With the toolbar slimmed down to
   * Flip H + Flip V (+ optional mobile-only Edit-text), space-between
   * pushed two buttons to opposite ends of the toolbar with a large
   * empty gap in the middle, which read as broken spacing rather than
   * deliberate layout. flex-start groups the action buttons together
   * on the left so the empty space on the right is the natural
   * unused area of a small action row. */
  justify-content: flex-start;
  border-bottom: 1px dashed var(--border);
  padding-bottom: 0.85rem;
}

.el-toolbar__btn {
  flex: 0 1 auto;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0.5rem 0.5rem;
  min-width: 48px;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.2px;
  transition: background 0.12s ease, color 0.12s ease, transform 0.12s ease;
}

.el-toolbar__btn:hover {
  background: var(--brand-pink-soft);
  color: var(--brand-pink-strong);
}

.el-toolbar__btn:active {
  transform: scale(0.96);
}

/* Busy state for action-row buttons that have an async backing
 * operation (currently just Background Removal). Reduces opacity and
 * disables hover so the button reads as "working, not interactive",
 * matches the native :disabled semantics, and keeps the cursor as
 * `wait` until the work completes. The spinner glyph below stands
 * in for the normal SVG icon during this state — vertical centering
 * is preserved because both icons render in roughly the same
 * 18×18 region. */
.el-toolbar__btn--busy,
.el-toolbar__btn:disabled {
  opacity: 0.55;
  cursor: wait;
}
.el-toolbar__btn--busy:hover,
.el-toolbar__btn:disabled:hover {
  background: transparent;
  color: var(--text-secondary);
}

/* Spinner used inside `.el-toolbar__btn` when an async operation is
 * in flight. Sized to roughly match the action-row SVG icons so the
 * layout doesn't jump when swapping between the two states. */
.el-toolbar__spinner {
  width: 16px;
  height: 16px;
  border: 2px solid var(--brand-pink-soft);
  border-top-color: var(--brand-pink);
  border-radius: 50%;
  animation: el-toolbar-spin 0.8s linear infinite;
}

@keyframes el-toolbar-spin {
  to { transform: rotate(360deg); }
}

.el-toolbar__btn--danger {
  color: var(--brand-pink-strong);
}

.el-toolbar__btn--danger:hover {
  background: #ffe4eb;
  color: #be1259;
}

/* Active-state variant. Used by toggle-shaped buttons (currently the
 * Snap-to-center toggle) to visually indicate "this mode is ON". The
 * palette mirrors the filter-chip active state below so the toolbar's
 * "a thing is currently turned on" affordance reads consistently no
 * matter which control it's attached to. Hover lands on the same
 * brand-pink-soft surface, so hovering an already-active button just
 * stays active rather than flashing into a different state. */
.el-toolbar__btn--active {
  background: var(--brand-pink-soft);
  color: var(--brand-pink-strong);
}

.el-toolbar__btn--active:hover {
  background: var(--brand-pink-soft);
  color: var(--brand-pink-strong);
}

.el-toolbar__btn-label {
  font-size: 10px;
  line-height: 1.1;
}

/* Layer controls section (mobile only) ----------------------------------
 *
 * Renders below the action row, above the sliders. Contains a small
 * uppercase label followed by a row of five buttons (Duplicate, Lock,
 * Move above, Move below, Delete). The wrapping container stacks
 * label-over-buttons; the inner row is flex with `space-between` so
 * the five buttons spread across the toolbar width rather than
 * clustering on the left like the action row does — the section is
 * its own thing with a clear visual boundary, not a continuation of
 * the actions above it.
 *
 * No own border/separator: the action row above already has
 * `border-bottom: 1px dashed` and `padding-bottom: 0.85rem`, plus
 * the parent's `gap: 0.85rem`, which together give a visible break
 * between actions and this section. Adding a top border here would
 * read as a doubled-up divider.
 *
 * The action buttons inside (.el-toolbar__btn) carry their own
 * styling from the rule above. At ≤480px the existing media query
 * hides `.el-toolbar__btn-label`, so on phones this row collapses
 * to icons only — by design, since five labelled buttons would not
 * fit on a narrow toolbar without wrapping. The icons (copy, lock,
 * chevron-up-in-square, chevron-down-in-square, trash) are chosen
 * to be legible without their labels. */
.el-toolbar__layer-section {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.el-toolbar__section-title {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.el-toolbar__layer-actions {
  gap: 0.4rem;
  justify-content: space-between;
}

/* Crop-mode toolbar face --------------------------------------------------
 *
 * When App enters crop mode for an image, ElementToolbar replaces its
 * normal layout with a title + Cancel / Apply pair. The same outer
 * .el-toolbar shell is kept so the floating panel's position and
 * shadow don't visibly shift when entering/leaving crop mode. */
.el-toolbar--crop {
  gap: 0.75rem;
}

.el-toolbar__crop-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: 0.2px;
}

.el-toolbar__crop-actions {
  display: flex;
  gap: 0.5rem;
}

.el-toolbar__crop-btn {
  flex: 1;
  padding: 0.65rem 0.8rem;
  border-radius: var(--radius-md);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

/* Primary action — Apply crop. Filled pink so the user reads it as
 * "this is the action that commits the in-progress change". */
.el-toolbar__crop-btn--primary {
  background: var(--brand-pink);
  color: #fff;
  border: 1px solid var(--brand-pink);
}

.el-toolbar__crop-btn--primary:hover {
  background: var(--brand-pink-strong);
  border-color: var(--brand-pink-strong);
}

/* Secondary action — Cancel. Outlined so it reads as the safe escape
 * route rather than competing visually with Apply. */
.el-toolbar__crop-btn--secondary {
  background: #fff;
  color: var(--text-primary);
  border: 1px solid var(--border);
}

.el-toolbar__crop-btn--secondary:hover {
  border-color: var(--brand-pink);
  color: var(--brand-pink-strong);
}

/* Sliders ------------------------------------------------------------------ */

.el-toolbar__sliders {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.el-toolbar__slider-row {
  display: grid;
  /* Three columns: label | slider | end-cluster (value + optional
   * reset button). The end cluster lives inside a single grid cell
   * so the value text and reset button can share their column via
   * an inner flex container (.el-toolbar__slider-end). Sizing the
   * end column differs between mobile and desktop — see the desktop
   * override below for the tightening rationale.
   *
   * Mobile default: fixed 72px reserves room for value (~44px) +
   * gap (~4px) + reset (24px), so the slider's effective width
   * doesn't jump as the user drags past a "changed from default"
   * threshold and the reset button appears or disappears. A jittery
   * slider under an active drag reads as a bug; the cost is ~24px
   * less horizontal room for the track, accepted on the narrow
   * mobile toolbar (where slider precision matters less than
   * stable layout). */
  grid-template-columns: 60px 1fr 72px;
  align-items: center;
  gap: 0.6rem;
}

/* Desktop — tighten the end column.
 *
 * Above the mobile breakpoint (matches App.jsx's MOBILE_QUERY of
 * 768px), the slider track gets the extra room back via
 * grid-template-columns ending in `auto`. The end cluster's width
 * is sized to its content: ~44px when only the value is visible,
 * ~72px when value + reset are both there. So:
 *
 *   • No reset shown → slider has 28px more room than mobile.
 *   • Reset shown    → slider has the same room as mobile.
 *
 * This means the slider WILL jitter ~28px when the user drags
 * across the "changed from default" threshold and the reset button
 * appears/disappears. Acceptable on desktop because (a) mouse
 * precision makes the crossing event rare during a drag, and (b)
 * the desktop bottom toolbar has more horizontal real estate that
 * benefits from the extra slider width when no reset is shown.
 * On mobile the jitter would be more annoying (touch is less
 * precise, more likely to cross thresholds) and the toolbar is
 * narrower, so the trade-off lands the other way — see above. */
@media (min-width: 769px) {
  .el-toolbar__slider-row {
    grid-template-columns: 60px 1fr auto;
  }
}

/* End cluster — holds the numeric value and (when changed-from-
 * default) the reset icon button. Flex so they sit side-by-side
 * with the value right-aligned within the cluster. The min-width
 * on the value (set below) keeps single/double/triple-digit
 * readouts from shifting the reset button's position as the user
 * drags.
 *
 * Fixed `min-height` matches the reset button's height (24px).
 * Without this, the row collapses to the slider+value's natural
 * height when no reset is visible (slider input ≈20px, value text
 * ≈15px), then expands to 24px the instant the reset appears as
 * the user crosses the "changed from default" threshold. That
 * vertical jiggle is especially visible when a user drags an
 * opacity / brightness / contrast / rotation slider back and forth
 * across the default value — the toolbar visibly grows and shrinks
 * by ~4px on each crossing, which reads as a glitch. Reserving the
 * larger of the two heights up-front keeps the row stable. The
 * extra height when no reset is showing is invisible (transparent
 * empty space at the right edge of the row) and harmless. */
.el-toolbar__slider-end {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.25rem;
  min-width: 0;
  min-height: 24px;
}

.el-toolbar__slider-label {
  font-size: 12px;
  color: var(--text-secondary);
  font-weight: 600;
}

.el-toolbar__slider {
  margin: 0;
}

/* Slider track wrapper — holds [start-icon] [slider] [end-icon].
 *
 * The native <input type="range"> is the only flex item that grows;
 * the two end icons take their content-width (~14px) and stay
 * pinned to the slider's left and right edges. `min-width: 0` on
 * this flex container lets the slider shrink with the parent grid
 * column rather than overflow it (without it, the implicit
 * min-content of <input type=range> is ~150px and would refuse to
 * shrink below that, breaking the toolbar layout on narrow screens).
 *
 * The wrapper itself occupies the same grid cell the bare <input>
 * used to occupy — it's a drop-in container around the existing
 * slider input. */
.el-toolbar__slider-track {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

.el-toolbar__slider-track .el-toolbar__slider {
  /* Slider takes all the remaining width inside the track wrapper.
   * Explicit min-width: 0 because flex items default to min-width:
   * auto, which for an <input> resolves to the input's intrinsic
   * min-content size and refuses to shrink. */
  flex: 1 1 auto;
  min-width: 0;
}

/* Slider end-icon hints (the dim/solid squares, sun glyphs, etc.
 * flanking each slider). See <SliderEndIcon> in ElementToolbar.jsx
 * for what gets rendered.
 *
 * Sized to match the reset button's footprint vertically (24px tall)
 * so the slider row's min-height check doesn't fluctuate between
 * "reset present" and "reset absent" states. The icon's 14×14 SVG
 * sits centered inside this 24×24 box, giving ~5px of padding on
 * each side — enough that the icon doesn't visually crowd the slider
 * track but not so much that the row stretches horizontally.
 *
 * Color matches `.el-toolbar__slider-label` (text-secondary) so the
 * icons read as informational labels rather than interactive
 * affordances. No hover state, no cursor: pointer — these are
 * decorative direction hints, not buttons.
 *
 * `flex: 0 0 auto` pins them to their natural width inside the flex
 * track wrapper, leaving the slider input to take the remaining
 * room.
 *
 * `pointer-events: none` so any stray click or tap intended for the
 * slider track edge passes through to the slider rather than getting
 * swallowed by the icon. The user expectation is that this end of
 * the track behaves like the slider itself for grab-and-drag
 * purposes — the icon shouldn't be a dead zone. */
.el-toolbar__slider-end-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 24px;
  color: var(--text-secondary);
  flex: 0 0 auto;
  pointer-events: none;
}

.el-toolbar__slider-value {
  font-size: 12px;
  color: var(--text-primary);
  font-weight: 600;
  text-align: right;
  /* Reserve min-width so single-digit ("5") vs triple-digit ("100")
   * readouts don't push the reset button around as the user drags.
   * 36px fits "100%" comfortably at the toolbar's 12px font size
   * with the right-aligned text settling consistently on the
   * right edge of the cell. */
  min-width: 36px;
}

/* Per-slider reset button — small icon-only affordance rendered
 * conditionally by ElementToolbar's <SliderResetButton> when the
 * slider's current value differs from its default (see
 * ElementToolbar.jsx for the detection logic). Lives inside
 * `.el-toolbar__slider-end` next to the numeric value, sharing
 * the slider row's end-cluster column via a flex container.
 *
 * No visible chrome until hover, so the button doesn't compete
 * with the slider/value for visual attention; on hover it gets
 * the same brand-pink-soft wash + brand-pink-strong ink that the
 * action-row buttons use, keeping the toolbar's interaction
 * language consistent.
 *
 * 24×24 square with the 14×14 SVG centered inside, leaving
 * roughly 5px of padding on each side — enough that the click
 * target stays comfortably tappable on mobile even though the
 * icon itself is small. */
.el-toolbar__slider-reset {
  background: transparent;
  border: none;
  padding: 0;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, transform 0.12s ease;
}

.el-toolbar__slider-reset:hover {
  background: var(--brand-pink-soft);
  color: var(--brand-pink-strong);
}

.el-toolbar__slider-reset:active {
  transform: scale(0.92);
}

/* Advanced (image-only) ---------------------------------------------------- */

.el-toolbar__advanced {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding-top: 0.4rem;
  border-top: 1px dashed var(--border);
}

.el-toolbar__advanced-btn {
  width: 100%;
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--brand-pink);
  background: var(--brand-pink-soft);
  color: var(--brand-pink-strong);
  font-weight: 600;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  transition: background 0.12s ease;
}

.el-toolbar__advanced-btn:hover {
  background: #fad0e0;
}

/* Background removal button (rendered inside this toolbar) ---------------- */

.el-toolbar__advanced .bg-removal-container {
  margin: 0;
  padding: 0;
  border: none;
}

/* Image filter chips (image-only) -----------------------------------------
 *
 * Compact horizontal row of toggleable filter presets. Selected chip uses
 * the brand-pink fill; unselected chips use a neutral wash. The icon glyph
 * is decorative — the label carries the meaning, since glyph aesthetics
 * are arbitrary. */
.el-toolbar__filters {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.el-toolbar__filter {
  flex: 1 1 auto;
  min-width: 56px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0.45rem 0.4rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: #fff;
  color: var(--text-secondary);
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.el-toolbar__filter:hover {
  border-color: var(--brand-pink);
  color: var(--brand-pink-strong);
}

.el-toolbar__filter.is-active {
  background: var(--brand-pink-soft);
  border-color: var(--brand-pink);
  color: var(--brand-pink-strong);
}

.el-toolbar__filter-icon {
  font-size: 14px;
  line-height: 1;
}

.el-toolbar__filter-label {
  font-size: 10px;
  letter-spacing: 0.2px;
}

@media (max-width: 480px) {
  .el-toolbar {
    padding: 0.7rem 0.85rem;
    border-radius: var(--radius-md);
  }
  .el-toolbar__btn {
    min-width: 42px;
    padding: 0.45rem 0.35rem;
  }
  .el-toolbar__btn-label {
    display: none;
  }
}

/* Hide the Size slider on desktop.
 *
 * On desktop the user has fine-grained corner-drag handles on the canvas
 * (the Konva transformer's keepRatio scale) plus arrow-key nudge, so the
 * slider is a redundant control. Hiding it tightens the floating toolbar
 * vertically without losing any capability. Mobile users can't easily
 * corner-drag a small touch target on a small screen, so the slider
 * stays as the primary size affordance there.
 *
 * The Opacity slider is intentionally kept on both desktop and mobile —
 * opacity has no corner-handle equivalent and the slider is its only
 * control surface. Only the Size row is hidden.
 *
 * Breakpoint matches App.jsx's MOBILE_QUERY (max-width: 768px) — the
 * same line that switches between mobile bottom-sheet and desktop
 * right-rail. Anything at ≥769px is the desktop layout, where the
 * canvas is the primary surface and corner drag is available. */
@media (min-width: 769px) {
  .el-toolbar__slider-row--size {
    display: none;
  }
}
