/* ═══════════════════════════════════════════════════════════════════════
   chart-v2-grid.css — CSS Grid layout for chart-v2 (Phase A-D redesign).
   ═══════════════════════════════════════════════════════════════════════
   Loaded ONLY when ?gridLayout=1 URL param is set (or once Phase D flips
   the default).  Coexists with the original flex-based layout in
   chart-v2.css; rules here override / replace just the layout-affecting
   parts of .suite-bars / .suite-bar / .suite-bar-row when the new mode is
   active (gated by `.suite-bars--grid` class on the bars container).

   Architecture:
     .suite-bars--grid                 ← outer container, holds rows
       .suite-row                      ← NEW: one wrapper per visual row
         .suite-row--stretched         ← OR
         .suite-row--natural
         .suite-bar  (grid item)       ← bar inside a row
           [via subgrid]
           .suite-bar-row--diagram     ← row within bar (subgrid)
             .suite-beat-cell          ← per-beat cell, grid-column = N
           .suite-bar-row--rhythm      ← (subgrid)
           .suite-bar-row--chord       ← (subgrid)
           .suite-bar-row--scales      ← (subgrid)

   Two CSS variables drive the entire layout:

     --suite-row-cols   on .suite-row      → column track template for the
                                          row (one fr-weighted track per bar)
     --suite-bar-cols   on .suite-bar      → column track template for the
                                          bar (one track per beat / sub-beat)

   Row stretch toggle = swap between .suite-row--stretched (grid with
   weighted fr columns) and .suite-row--natural (flex with content-sized
   bars).  Single CSS-class toggle, no JS cascade.

   Manual drag preserved: drag handlers update bar.widthFactor /
   beat.widthFactor / subBeat.widthFactor in the model, then re-emit
   the appropriate --suite-bar-cols / --suite-row-cols variable.  No flex
   rewrite, no per-bar inline style proliferation.
   ─────────────────────────────────────────────────────────────────── */

/* ── First-paint flash gate ────────────────────────────────────────
   Editor.rerender adds `.is-loading` to `.suite-root` on the first
   render of an Editor instance and removes it after the post-mount
   cascade (fit + engraver + diagram align + beams + ties) has
   settled.  Hides the chart from view during the brief moment the
   shell is laid out at natural width without scale + pixel widths
   applied — eliminates the "small/jumbled then settles" flash on
   hard-refresh.  In-session re-renders don't gate (they're already
   laid out correctly relative to the previous paint).  CSS variable
   `--suite-loading-bg` lets theme/CMS override the placeholder
   colour. */
.suite-root.is-loading {
  visibility: hidden;
}

/* ── Outer container ─────────────────────────────────────────────── */
.suite-bars--grid {
  /* Single design token — minimum gap between any barline and the
     leading edge of any row's content. */
  --suite-leading-inset: 6px;
  /* Tsig clearance gaps.  Both tight by default — chord text and
     rhythm glyphs are allowed to sit close to the tsig so they
     visually centre under the diagram (user-stated 2026-05-04).
     CMS-overridable via the chart root for sites that prefer more
     breathing room.  Tokens kept separate because the rules differ
     conceptually (chord = same row as tsig host; rhythm = the row
     hosting tsig itself).
     - --suite-chord-tsig-gap   (chord row hosting tsig)
     - --suite-rhythm-tsig-gap  (rhythm row hosting tsig — H1) */
  --suite-chord-tsig-gap: 2px;
  --suite-rhythm-tsig-gap: 4px;
  /* Barline overhang — how far above the topmost visible content (and
     below the bottommost, before the bar.bottom clamp) the barline
     extends.  6 px clears diagrams' open-position dot markers at the
     top; the bottom is clamped to bar.bottom so the line never spills
     into the wrap-gap region.  Locked rule (2026-05-04). */
  --suite-barline-top-inset: 6px;
  /* Inter-row gap variables (between adjacent content rows inside a
     bar).  Declared on the GRID so the *-hidden class rules below can
     override them via specificity.  Putting these on .suite-row would
     let the row's own declaration trump the grid's class-based
     zero-out (same specificity, but the row's rule comes later in the
     stylesheet) — which is exactly the bug being fixed: section B
     (chord+scales only) had its diagram-row + diagram-gap tracks
     re-inflated to 8 px by .suite-row's own var(--suite-bar-row-gap)
     declaration, leaving big empty space below the section header. */
  /* gap-* tokens kept for backward-compat with code that reads them;
     2026-05-04 refactor moved bar-row stacking to flex-column on
     .suite-bar with a single gap, so these aren't load-bearing anymore. */
  --suite-gap-dr: var(--suite-bar-row-gap, var(--suite-row-gap, 8px));
  --suite-gap-rc: var(--suite-bar-row-gap, var(--suite-row-gap, 8px));
  --suite-gap-cs: var(--suite-bar-row-gap, var(--suite-row-gap, 8px));

  display: flex;
  flex-direction: column;
  /* Vertical gap between visual rows of bars (when wrapping at
     bars-per-line).  Tunable via the variable for theme / CMS. */
  gap: var(--suite-row-stack-gap, 40px);
  padding-left: 1px;
  overflow: visible;
}

/* When a section hides the rhythm row (tsig falls to chord row),
   bars are ~48 px tall (chord row hosts tsig).  Default 40 px
   row-stack-gap looks proportionally too large for short bars —
   tighten it so the section reads compact, matching the legacy
   reference (H2).  Triggered on `rhythm-hidden` (not `diagram-
   hidden`) — empty diagram rows alone don't toggle the latter. */
.suite-bars--grid.suite-bars--rhythm-hidden {
  --suite-row-stack-gap: 20px;
}
/* Same idea for chord-only (no rhythm, no scales) — even tighter
   since bars are ~48 px tall with no scales row. */
.suite-bars--grid.suite-bars--rhythm-hidden:not(.suite-bars--scales-shown) {
  --suite-row-stack-gap: 14px;
}

/* ── Add-bar placeholder (the dashed "+") ────────────────────────── */
/* Flex-column children default to cross-axis stretch — that would make
   the placeholder span the entire chart width.  Pin it to a sensible
   natural width (matches roughly one bar's beat allocation) and align
   to the start so it sits flush with the leading barline.  Falls back
   to its own intrinsic width on narrow viewports via max-width. */
.suite-bars--grid > .suite-bar-add {
  align-self: flex-start;
  width: 200px;
  max-width: 100%;
}

/* ── Row wrapper ─────────────────────────────────────────────────── */
/* Each `.suite-row` sizes naturally to its visible bar content.  Inter-
   row-of-bars distance is governed by `--suite-row-stack-gap` (the gap
   on `.suite-bars--grid`), which IS constant across the chart — that
   alone gives uniform bottom-to-top distance between rows regardless
   of content density.  No row min-height: short-content sections
   (chord + scales only) get a tight row + the standard wrap gap, not
   the 4-row footprint padded out with empty space. */
.suite-bars--grid .suite-row {
  position: relative;
  display: grid;
  /* Default: equal-width tracks; computed value comes from --suite-row-cols. */
  grid-template-columns: var(--suite-row-cols, 1fr);
  /* Single row track sized to content.  Each .suite-bar inside is a
     flex column that stacks its visible bar-rows + separators with
     a small gap.  Hidden rows (`display: none`) contribute zero
     height naturally — no phantom space, no JS-managed
     grid-template-rows write needed (locked rule G1 structural
     fix shipped 2026-05-04: `.suite-bar-content` flex-column wrapper
     replaces the prior subgrid + per-row JS gridTemplateRows
     manipulation).  Cross-bar column alignment is preserved via
     --suite-row-cols on this .suite-row; cross-row vertical alignment
     of rhythm baselines is preserved because every .suite-row uses
     the same content-sized rhythm track height. */
  grid-template-rows: auto;
  gap: 0;
}

/* Both stretched + natural variants — engraver writes per-bar pixel
   widths to `--suite-bar-cols` AND the matching row template to
   `--suite-row-cols` so row tracks deterministically sum to rowWidth
   (stretch modes) or sum-of-naturals (fit mode).  CSS Grid's
   `max-content` track sizing on a row whose grid items have their
   own pixel templates sub-pixel-rounds in ways that don't sum back
   to the parent width — taking ownership of the row template is the
   only way to guarantee cross-row barline alignment.

   Fallback `repeat(…, max-content)` covers the brief moment between
   row mount and the first engraver pass; it's overwritten as soon
   as the engraver runs. */
.suite-bars--grid .suite-row--stretched,
.suite-bars--grid .suite-row--natural {
  grid-template-columns: var(
    --suite-row-cols,
    repeat(var(--suite-bar-count, 4), max-content)
  );
}

/* ── Bar — flex column wrapper ───────────────────────────────────
   Bar is a flex column containing its visible bar-rows + separators.
   Each child row is its own grid (column layout via --suite-bar-cols);
   the bar itself only manages vertical stacking.  Hidden rows
   (display:none) take zero height naturally → bar height = sum of
   visible row heights + inter-row gaps.  Replaces the prior
   `display: grid; grid-template-rows: subgrid; grid-row: 1/-1`
   pattern that depended on per-row JS gridTemplateRows writes for
   the row-track collapse to fire.  Locked rule G1 structural fix,
   2026-05-04. */
.suite-bars--grid .suite-bar {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Padding lives INSIDE the bar's allocated width — the engraver
     hands each bar a pixel allocation derived from row.clientWidth,
     and content-box would add the bar's padding (10 px horizontal) on
     TOP of that, pushing the row's bar sum past row.clientWidth and
     spilling the rightmost bar past the section edge.  border-box
     keeps the bar's outer box equal to the engraver's allocation. */
  box-sizing: border-box;
  border: 0;
  padding: 6px 8px 8px 2px;
  min-width: 0;
  width: 100%;
  overflow: visible;
  /* Inter-row gap inside the bar — single design token. */
  gap: var(--suite-bar-row-gap, var(--suite-row-gap, 8px));
}

/* ── Bar rows ─────────────────────────────────────────────────── */
/* Each .suite-bar-row spans the full bar width and re-declares the
   bar's column template directly.  We tried `grid-template-columns:
   subgrid` first (single source of truth) but Safari has a bug where
   subgrid grid-items that are themselves flex containers don't honour
   `justify-self: stretch` — cells stayed at their 8 px content width
   instead of expanding to the subgrid track.  Re-declaring the
   template here avoids the subgrid path entirely.  Identical
   templates + identical row widths still produce identical column
   widths across rows, so cells in different rows align column-wise
   without subgrid (verified with devtools diagnostic). */
.suite-bars--grid .suite-bar-row {
  display: grid;
  grid-template-columns: var(--suite-bar-cols, max-content);
  grid-column: 1 / -1;
  align-items: center;
  gap: 0;
}
.suite-bars--grid .suite-row--natural .suite-bar-row {
  grid-template-columns: var(--suite-bar-cols-nat, max-content);
}

/* `height` (not min-height): forces every bar's diagram row to the
   identical chart-wide max written by _normaliseDiagramRowHeight, so
   `align-items: flex-end` actually bottom-aligns svgs across bars.
   Earlier `min-height` was producing rows that shrank to their
   svg's natural height when the svg was shorter than the chart-wide
   max — bar 10's row was 124.7 px while bar 9's was 133.7 — leaving
   svgs at different Y positions in the same visual row. */
.suite-bars--grid .suite-bar-row--diagram { height: var(--suite-diagram-row-h, 64px); align-items: flex-end; }
.suite-bars--grid .suite-bar-row--rhythm  { height: var(--suite-rhythm-row-h, 48px); align-items: flex-end; opacity: 0.85; }
/* Chord + scales bar-rows: min-height defaults to 0 so the track sizes
   PURELY to its content (chord text / scale chip height).  Without
   this override, the legacy `.suite-bar-row--chord { min-height: 36px }`
   (chart-v2.css line 791) would keep the bar-row at 36 px, which then
   feeds back into the parent grid's max-content track sizing — leaving
   ~16 px of empty space below the chord text and above the separator
   in chord-only sections (the user-reported "empty row" bug, 2026-05-03). */
.suite-bars--grid .suite-bar-row--chord   { min-height: var(--suite-chord-row-h,  0px); padding-top: 0; align-items: center; }
.suite-bars--grid .suite-bar-row--scales  { min-height: var(--suite-scales-row-h, 0px); padding-top: 0; align-items: flex-start; }

/* When a row hosts the tsig (rhythm hidden → tsig falls to chord
   row, chord+rhythm hidden → tsig falls to scales row, etc.), the
   row must be tall enough to fit the 38 px SMuFL tsig glyph
   without clipping or overflowing into the next row.  User-stated
   2026-05-04: keep the tsig at default size, grow the host row
   instead.  48 px matches the rhythm row's natural height (the
   normal tsig host) — same engraving feel across host rows.
   Applied at the SECTION level (.suite-bars--grid) so EVERY bar's
   chord row in the section gets the host height — non-tsig bars
   in the same section keep vertical alignment with the tsig bar
   (separator + chord + scale row positions match). */
.suite-bars--grid.suite-bars--rhythm-hidden .suite-bar-row--chord {
  min-height: 48px;
}
.suite-bars--grid.suite-bars--rhythm-hidden.suite-bars--chord-hidden .suite-bar-row--scales {
  min-height: 48px;
}

/* Hide rows.  Bar is now a flex column (G1 structural fix 2026-05-04),
   so display:none on a row removes it from the flex stack and
   contributes zero height — no need to zero CSS variables. */
.suite-bars--grid.suite-bars--diagram-hidden .suite-bar-row--diagram { display: none; }
.suite-bars--grid.suite-bars--rhythm-hidden  .suite-bar-row--rhythm  { display: none; }
.suite-bars--grid.suite-bars--chord-hidden   .suite-bar-row--chord   { display: none; }
.suite-bars--grid:not(.suite-bars--scales-shown) .suite-bar-row--scales { display: none; }

/* ── Row separators ─────────────────────────────────────────────── */
/* Separators are flex children of .suite-bar (after the structural
   refactor 2026-05-04).  Order via CSS `order` set by BarView_grid
   between two visible rows.  Hidden by default; `.suite-root.row-seps`
   on the chart root reveals them. */
.suite-bars--grid .suite-bar-row-sep {
  height: 0;
  display: none;
  margin: 2px 8px;
  border-top-width: 1px;
  border-top-style: var(--suite-rowsep-style, dashed);
  border-top-color: var(--suite-rowsep-color, #888);
}
.suite-root.row-seps .suite-bars--grid .suite-bar-row-sep { display: block; }

/* ── Beat cell — single column ──────────────────────────────────── */
.suite-bars--grid .suite-beat-cell {
  display: flex;
  justify-content: center;
  min-width: 0;
  overflow: visible;
  width: 100%;
  justify-self: stretch;
  /* No global `align-items` — vertical alignment is per-row-type
     (rhythm = bottom for engraver stem-up convention, diagram + chord
     = centre, scales = top).  Setting `align-items: center` here would
     override every per-row rule and put rhythm glyphs in the middle of
     the rhythm track instead of on its baseline. */
}
/* Per-row-type vertical alignment (engraving convention).
   Diagram cells bottom-align: every diagram (regardless of intrinsic
   height — small triads, fat 4-fret voicings, voicings with an extra
   fingering-numbers row below the dot grid) sits with its BOTTOM
   flush to the row track's bottom edge.  Result: the dashed
   diagram→rhythm separator stays a constant distance from the
   diagrams' lowest pixel across every bar in the row, even when the
   diagrams have wildly different vertical extents.  Locked rule
   (2026-05-03): diagrams are bottom-anchored, never centered. */
/* Rhythm cell: cell-centre alignment.  The rhythm-glyph wrapper sits
   at cell-centre by default; the JS pass `_alignRhythmUnderDiagram`
   measures the diagram's centre X (when present) and writes a
   `transform: translateX()` on the glyph wrapper to align it with
   the diagram, while enforcing the H1 tsig-clearance floor.  Locked
   rule G6 (2026-05-04): no primitive `padding-left` for cross-row
   alignment — use intelligent JS placement instead. */
.suite-bars--grid .suite-bar-row--rhythm  > .suite-beat-cell {
  align-items: flex-end;
  padding-bottom: 2px;
  justify-content: center;
}
/* Whole-bar override — when the bar contains a SINGLE rhythm cell
   (1 beat, no sub-beats), left-align rhythm glyph AND chord name to
   the bar's leading edge instead of centring.  `.suite-bar.is-whole-bar`
   is set in bar-view.js.  Per user 2026-05-13: "a 'whole tone'
   slash or note shouldn't be centered, but left aligned" + chord
   name follows. */
.suite-bars--grid .suite-bar.is-whole-bar .suite-bar-row--rhythm > .suite-beat-cell,
.suite-bars--grid .suite-bar.is-whole-bar .suite-bar-row--chord  > .suite-beat-cell {
  justify-content: flex-start;
}
.suite-bars--grid .suite-bar.is-whole-bar .suite-chord-display {
  left: 0;
  transform: translateY(calc(-50% + var(--suite-chord-vshift, 3px)));
}
.suite-bars--grid .suite-bar.is-whole-bar .suite-chord-input {
  text-align: left;
}
.suite-bars--grid .suite-bar-row--diagram > .suite-beat-cell { align-items: flex-end; }
.suite-bars--grid .suite-bar-row--chord   > .suite-beat-cell { align-items: center; }
.suite-bars--grid .suite-bar-row--scales  > .suite-beat-cell { align-items: flex-start; }

/* ── Scale row cells — content-aware overflow + truncate as last resort
   ────────────────────────────────────────────────────────────────────
   Scale chips display at their NATURAL width by default and are
   permitted to overflow visibly into adjacent cells — a chip in beat 1
   can extend past the beat-1 cell boundary and use the leading
   whitespace of beat 2 (or wherever empty space exists), the same way
   chord text already overflows in the chord row.  When a chip would
   collide with the NEXT chip's leading edge, an editor-layout JS pass
   (_balanceScaleChipOverflow) measures the gap and inline-truncates
   only that chip via `max-width` + `text-overflow: ellipsis`.  Static
   CSS clipping is intentionally absent — without it, the engraver
   measures true natural chip widths (instead of echoing previous
   pass's allocations) and the chip's `title=` tooltip preserves the
   full name on hover for the rare truncated case.  */
.suite-bars--grid .suite-bar-row--scales .suite-beat-cell {
  overflow: visible;
  align-items: flex-start;
  /* Horizontal alignment of scale chips inside their cell.  Default
     is `flex-start` so chips sit flush with the bar's leading edge
     (engraving convention: scale labels anchor to the start of the
     chord they apply to).  Overridable per-chart via the Display
     popover, which writes `--suite-scale-align` on the chart root. */
  justify-content: var(--suite-scale-align, flex-start);
}
.suite-bars--grid .suite-bar-row--scales .suite-beat-scales {
  display: flex;
  gap: 2px 4px;
  white-space: nowrap;
}
/* ── Scale-chip overflow + hover (CMS-themable) ────────────────────
   JS pass `_balanceScaleChipOverflow` writes:
     • `data-truncated="soft"` — chip clamped to fit before next chip
     • `data-truncated="hard"` — chip has no useful room (avoid show)
     • `--suite-chip-clamp`     — measured max-width in px
   CSS reads those declaratively.  No inline visual-property assignments
   in JS, no `!important` here — hover wins by attribute-selector
   specificity.  Design tokens (background, shadow, radius) live in
   `--suite-chip-*` custom properties so theme / admin overrides
   propagate without touching this rule. */
.suite-bars--grid .suite-bar-row--scales .suite-scale-chip {
  white-space: nowrap;
  border-radius: var(--suite-chip-radius, 4px);
}

/* ── Scale-zone background tinting (chord-2-inherits-from-chord-1) ─
   Renderer stamps `data-scale-zone="1|2|3|4"` on each scale cell
   that belongs to a scale zone (a contiguous run of beats sharing
   the same applied scale).  Adjacent cells in the same zone paint
   a uniform tint across the row; zone boundaries appear as a soft
   colour change.
   Token tints are LUXURIOUS — soft warm/cool/neutral hues at low
   opacity, so they read as "embraced" rather than "highlighted".
   CMS-overridable per chart via `--suite-scale-zone-{1..4}-bg`. */
.suite-bars--grid {
  /* Light-mode defaults — soft warm/cool/neutral tints layered over
     the paper.  Hand-picked to evoke "luxurious" — like aged paper
     with a subtle ink wash, not neon highlights. */
  --suite-scale-zone-1-bg: rgba(201, 127, 59, 0.07);   /* warm ochre */
  --suite-scale-zone-2-bg: rgba(80, 130, 180, 0.07);   /* soft slate-blue */
  --suite-scale-zone-3-bg: rgba(150, 100, 160, 0.07);  /* dusty mauve */
  --suite-scale-zone-4-bg: rgba(110, 150, 100, 0.07);  /* sage green */
}
[data-theme="dark"] .suite-bars--grid {
  /* Dark mode — same hues but slightly stronger to read against
     the deep paper background.  Opacity ~0.12 keeps it luxurious. */
  --suite-scale-zone-1-bg: rgba(231, 160, 95, 0.11);
  --suite-scale-zone-2-bg: rgba(120, 170, 220, 0.11);
  --suite-scale-zone-3-bg: rgba(180, 130, 200, 0.11);
  --suite-scale-zone-4-bg: rgba(140, 190, 130, 0.11);
}
/* Zone overlay divs — absolute-positioned children of
   .suite-bar-row--scales.  Each overlay's left+width is computed by
   JS as an equal slice of the row's width (so two zones split the
   bar 50/50, three split 33/33/33, etc.).  Sits at z-index 0
   behind the scale cells (z-index 1).  Subtle inner glow + rounded
   corners give the "luxurious" tint feel; dark-mode-aware tint
   colours come from --suite-scale-zone-{1..4}-bg tokens above. */
.suite-bars--grid .suite-bar-row--scales {
  position: relative;
}
.suite-bars--grid .suite-bar-row--scales > .suite-beat-cell {
  position: relative;
  z-index: 1;
}
.suite-bars--grid .suite-scale-zone-bg {
  position: absolute;
  top: 2px;
  bottom: 2px;
  z-index: 0;
  border-radius: 4px;
  box-shadow:
    inset 0  1px 0 rgba(255, 255, 255, 0.06),
    inset 0 -1px 0 rgba(0,   0,   0,   0.03);
  pointer-events: none;
}
.suite-bars--grid .suite-scale-zone-bg[data-scale-zone="1"] { background: var(--suite-scale-zone-1-bg); }
.suite-bars--grid .suite-scale-zone-bg[data-scale-zone="2"] { background: var(--suite-scale-zone-2-bg); }
.suite-bars--grid .suite-scale-zone-bg[data-scale-zone="3"] { background: var(--suite-scale-zone-3-bg); }
.suite-bars--grid .suite-scale-zone-bg[data-scale-zone="4"] { background: var(--suite-scale-zone-4-bg); }

/* In EDIT mode the scale cell stacks each chip + the "+ add"
   button on its own row, with a small gap between them.  The
   `+ add` button additionally has horizontal whitespace from the
   chip's leading edge so it reads as a separate affordance.  Read
   mode keeps the chips inline (no +add button there).  Column
   layout also prevents the JS chip-overflow balancer from
   truncating the first chip when the second one is added — each
   chip has its own row, no horizontal collision possible. */
.suite-root:not(.suite-root--read) .suite-bars--grid .suite-beat-scales {
  flex-direction: column;
  align-items: flex-start;
  gap: 5px;
}
.suite-root:not(.suite-root--read) .suite-bars--grid .suite-beat-scales > .suite-scale-add {
  /* Vertical breathing room (margin top + bottom) so the dashed
     border doesn't kiss the top / bottom edge of the +add zone
     band.  Horizontal whitespace from the leading edge so the
     button doesn't hug the barline. */
  margin: 4px 0 4px 12px;
}

/* The .suite-scale-sep div inside the cell is a positional marker
   only — the actual visible separator appears as a paper-coloured
   horizontal strip masked into the zone bg overlay (see the
   `linear-gradient` rule below).  Keeping the marker so the
   flex layout still reserves height for the gap between chip and
   +add button. */
.suite-bars--grid .suite-scale-sep {
  flex: 0 0 100%;
  width: 100%;
  height: 4px;
  margin: 0;
  pointer-events: none;
}
/* Edit mode: paint a paper-coloured horizontal strip across the
   zone bg so it splits visually into TWO bands — one behind the
   scale chip(s), one behind the "+ add" button.  Position is
   approximate (28-33 px from row top) — works for typical chip
   + sep + add stack heights.  Read mode renders zones as a
   single solid band (no +add button there). */
.suite-root:not(.suite-root--read) .suite-bars--grid .suite-scale-zone-bg[data-scale-zone] {
  background-image: linear-gradient(
    to bottom,
    transparent 0,
    transparent 28px,
    var(--surface, #fdfaf2) 28px,
    var(--surface, #fdfaf2) 33px,
    transparent 33px
  );
}
/* Keep the chip's natural width — never let the column layout
   stretch it across the full cell, otherwise the bg-tint band
   would visually merge with the chip's own background.  Also
   defeat the truncate-pass which assumes a horizontal row. */
.suite-root:not(.suite-root--read) .suite-bars--grid .suite-bar-row--scales .suite-scale-chip,
.suite-root:not(.suite-root--read) .suite-bars--grid .suite-bar-row--scales .suite-scale-chip[data-truncated] {
  max-width: none;
  overflow: visible;
  text-overflow: clip;
}

/* ── Leading-edge anchor (HARD RULE) ───────────────────────────────
   Locked rule (replaces the earlier "vertical-centering principle"):
   no row's content may cross a barline.  Each row's leading content
   sits at "barline + leading-inset" — a single design token shared
   across rows so chord names, diagrams, scale labels, and rhythm
   glyphs all anchor at the same X relative to the leading barline.

   `--suite-leading-inset` is the single source of truth.  CMS / theme
   overrides retune the gap globally without touching selectors.

   Implementation:
     • Chord + diagram rows switch from centre-anchored to LEFT-
       anchored (justify-content: flex-start on the cell + override
       on `.suite-chord-display` further down).
     • All four row types receive `padding-left: var(--suite-leading-
       inset)` on the LEADING cell of each bar.  When a row hosts the
       tsig, the tsig column ALREADY supplies leading space, so the
       padding is suppressed there.
     • The engraver (engraver-layout.js _measureBar) reads each
       cell's computed horizontal padding and adds it to that cell's
       content allocation, so the inset is RESERVED space — content
       never has to fight for room with the inset.
     • Rhythm row stays centre-aligned in its cell so beam / tie
       geometry (cell-rect.center + STEM_X_OFFSET) keeps working;
       the leading inset is still applied so beat 1's notehead can
       never sit on top of the barline. */
.suite-bars--grid .suite-bar-row > .suite-beat-cell:first-child {
  padding-left: var(--suite-leading-inset, 6px);
}
/* When the row hosts the tsig, the tsig column itself provides the
   leading offset — no extra inset on the cell.  (Tsig priority set
   by BarView_grid: rhythm > chord > scales > diagram.) */
.suite-bars--grid .suite-bar-row.has-tsig > .suite-beat-cell:first-child {
  padding-left: 0;
}

/* In a tsig bar, the tsig column (col 1, ~22 px) is only physically
   occupied by the tsig glyph in the HOST row.  In every other row
   that column is empty space — wasted real estate that pushes the
   diagram, chord, and scale labels away from the bar's leading
   barline.  Reclaim it: pull the first cell of non-host rows
   leftward by the tsig column width so its content sits at the
   bar's content-left edge (with the standard leading-inset
   breathing room).  Single design token (`--suite-tsig-row-shift`);
   the engraver's per-cell content measurement is unaffected
   because margin doesn't change intrinsic content width. */
.suite-bars--grid .suite-bar.has-tsig
  .suite-bar-row:not(.has-tsig)
  > .suite-beat-cell:first-child {
  margin-left: var(--suite-tsig-row-shift,
                   calc(-1 * var(--tsig-width, 22px)));
}
/* (Earlier the engraver reserved the tsig column on all bars in
   diagram-hidden mode for cross-bar X uniformity, which required
   disabling the tsig-row-shift here.  Reverted: chord/scale text
   on non-tsig bars now starts at barLeft + leading-inset (~6 px)
   to honour the user's "small gap from left barline" preference.
   The remaining tsig bar's own host-row chord X is at barLeft +
   tsig-width + leading-inset — the tsig genuinely pushes its
   first beat in its own row, by design.) */

/* (No trailing-inset on the last cell — the bar's own padding-right
   already provides clearance before the trailing barline.  Adding
   another inset doubled the gap unnecessarily.) */

/* Chord + diagram rows: CENTRED content under their cell midline so
   the chord text sits directly below the rhythm symbol it belongs to
   (the rhythm cell is also cell-centred).  Locked rule (2026-05-04,
   user-stated): chord names are visually tied to their rhythm; left-
   alignment loses that reference.  Diagrams keep their bottom-anchor
   for the separator alignment rule (independent concern).

   Long chord names overflow the cell symmetrically (both sides) via
   the absolute-positioned `.suite-chord-display` span — `overflow:
   visible` on the cell + the absolute span means a wide chord
   crosses into adjacent cells equally, preserving the centre-anchor
   under the rhythm note.  When two adjacent chord cells' text
   overlaps, a JS pass shifts/expands the affected cells; ellipsis
   truncation is the last resort. */
/* Chord cell: centred under the rhythm symbol (the rhythm cell is
   also centred).  Long chord names overflow visibly into adjacent
   cells; `_clampChordOverflowToBars` shifts the chord-display in
   the opposite direction when it would cross a barline. */
.suite-bars--grid .suite-bar-row--chord   > .suite-beat-cell {
  justify-content: center;
}
/* Diagram cell: LEFT-anchored at cell-left + leading-inset.  The
   diagram is structural content tied to the BEAT, not to the chord
   text below it — its X position should be predictable per beat
   regardless of cell width or how long the chord name is.  Locked
   rule (2026-05-04, user-stated): diagram is independent of chord
   text; if the chord text needs more space, COMPENSATE THE TEXT
   (shift / truncate), never move the diagram. */
.suite-bars--grid .suite-bar-row--diagram > .suite-beat-cell {
  justify-content: flex-start;
  padding-left: var(--suite-leading-inset, 6px);
}

/* Centre-anchored chord-display.  Vertical centre + small downward
   shift (--suite-chord-vshift, default 3 px) compensates for the
   serif font's line-box asymmetry — the visible glyphs sit ABOVE
   the line-box midpoint because of descender-area padding, so a
   pure 50% center reads as "text too high".  The shift puts the
   visible glyph midpoint at the row's optical centre, between the
   separator lines.  CMS-overridable. */
.suite-bars--grid .suite-chord-display {
  left: 50%;
  --suite-chord-vshift: 3px;
  transform: translate(-50%, calc(-50% + var(--suite-chord-vshift, 3px)));
}
.suite-bars--grid .suite-chord-input {
  text-align: center;
}

/* RHYTHM-HIDDEN sections (chord-only / chord+scales).  Two
   sub-cases by per-bar diagram presence:
   - Bar HAS diagrams (data-has-diagrams="true"): chord/scale
     stay CENTRED so they label the diagram visually (locked B3).
   - Bar does NOT have diagrams: chord/scale go LEFT-aligned —
     lyric-sheet convention; every bar shows the same X offset
     for chord + scale.  User-stated 2026-05-04: "left alignment
     only takes prio when the diagram is inactive". */
.suite-bars--grid.suite-bars--rhythm-hidden .suite-bar:not([data-has-diagrams]) .suite-chord-display {
  left: 0;
  transform: translateY(calc(-50% + var(--suite-chord-vshift, 3px)));
}
.suite-bars--grid.suite-bars--rhythm-hidden .suite-bar:not([data-has-diagrams]) .suite-chord-input {
  text-align: left;
}
.suite-bars--grid.suite-bars--rhythm-hidden .suite-bar:not([data-has-diagrams]) .suite-bar-row--chord > .suite-beat-cell {
  justify-content: flex-start;
}
/* Bars WITH diagrams in rhythm-hidden sections: scales also centre
   under the diagrams so chord+scale read as a unit. */
.suite-bars--grid.suite-bars--rhythm-hidden .suite-bar[data-has-diagrams] .suite-bar-row--scales > .suite-beat-cell {
  justify-content: center;
}

.suite-bars--grid .suite-bar-row--scales .suite-scale-chip[data-truncated] {
  max-width:     var(--suite-chip-clamp, 100%);
  overflow:      hidden;
  text-overflow: ellipsis;
}
/* Hover lifts the chip out of its truncated state and paints a soft
   readable card.  Layout footprint unchanged (margin negates padding,
   position:relative + z-index keeps it above neighbours). */
.suite-bars--grid .suite-bar-row--scales .suite-scale-chip[data-truncated]:hover {
  max-width:     none;
  overflow:      visible;
  text-overflow: clip;
  background:    var(--suite-chip-hover-bg,    var(--accent-bg, #fff3e6));
  box-shadow:    var(--suite-chip-hover-shadow, 0 1px 4px rgba(0, 0, 0, 0.15));
  padding:       0 var(--suite-chip-hover-pad, 6px);
  margin:        0 calc(var(--suite-chip-hover-pad, 6px) * -1);
  position:      relative;
  z-index:       var(--suite-chip-hover-z, 10);
}

/* ── Time signature column ──────────────────────────────────────── */
/* When showTsig is true, the bar's --suite-bar-cols includes a leading
   --tsig-width track.  The .suite-bar-tsig element occupies grid column 1,
   spanning all four rows so it visually centres against the rhythm row. */
.suite-bars--grid .suite-bar.has-tsig .suite-bar-tsig {
  grid-column: 1;
  grid-row: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--tsig-width, 22px);
  z-index: 2;
}

/* ── Pickup spacer (right-aligns beats) ─────────────────────────── */
.suite-bars--grid .suite-bar.is-pickup {
  /* Pickup bars right-align — leading whitespace is the "anacrusis gap". */
  /* Implemented via an empty leading spacer column in --suite-bar-cols. */
}

/* ── Drag handle on right edge ──────────────────────────────────── */
.suite-bars--grid .suite-bar-resize {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 6px;
  cursor: col-resize;
  background: transparent;
  z-index: 3;
}
.suite-bars--grid .suite-bar-resize:hover { background: var(--accent, #c85a1a); opacity: 0.4; }
.suite-root--read .suite-bars--grid .suite-bar-resize { display: none; }

/* ── Bar-level chrome (number, gear, line-break, repeat marks) ─── */
/* These are absolutely-positioned overlays on the bar — not part of
   the grid layout.  Same as in flex mode. */
.suite-bars--grid .suite-bar-num,
.suite-bars--grid .suite-bar-gear,
.suite-bars--grid .suite-bar-tempo,
.suite-bars--grid .suite-bar-linebreak,
.suite-bars--grid .suite-bar-line {
  position: absolute;
  z-index: 2;
}

/* Read mode hides edit-only chrome (same as flex layout). */
.suite-root--read .suite-bars--grid .suite-bar-gear,
.suite-root--read .suite-bars--grid .suite-bar-linebreak,
.suite-root--read .suite-bars--grid .suite-bar-resize { display: none; }
