.preview-section {
   padding: 0 0 28px;
   margin-bottom: 28px;
   border-bottom: 1px solid var(--line);
}

.preview-section:last-child {
   border-bottom: 0;
   margin-bottom: 0;
}
.preview-section.is-active {
   position: relative;
}
.preview-section.is-active::before {
   content: "";
   position: absolute;
   top: 0;
   bottom: 28px;
   left: -14px;
   width: 3px;
   border-radius: 3px;
   background: var(--lime);
}

.section-tools {
   display: flex;
   align-items: center;
   gap: 12px;
}
.preview-header p {
   margin-top: 9px;
}
.artist-value {
   font-style: italic;
}
.upload-project {
   display: inline-flex;
   align-items: center;
   cursor: pointer;
}
#projectFileInput {
   display: none;
}
.palette-group {
   padding: 11px;
   border: 1px solid #d8e9dc;
   border-radius: 10px;
   background: #f8fcf9;
}
.palette-label {
   display: block;
   margin-bottom: 8px;
   color: #6d8675;
   font-size: 10px;
   font-weight: 800;
   letter-spacing: 0.09em;
   text-transform: uppercase;
}
.chord-root-picker {
   display: grid;
   grid-template-columns: repeat(6, 36px);
   gap: 7px;
   margin-bottom: 0;
}
.chord-root-picker .key {
   width: 36px;
   height: 34px;
   padding: 0;
   border-color: #d6e8da;
   background: #fff;
   font-size: 12px;
}
.chord-root-picker .key.active {
   border-color: var(--green);
   background: var(--green);
   color: #fff;
   box-shadow: 0 2px 5px rgba(31, 112, 74, 0.18);
}
.palette-divider {
   height: 1px;
   margin: 13px 5px;
   background: #d6e4d8;
   position: relative;
}
.palette-divider span {
   position: absolute;
   top: -1px;
   left: 0;
   width: 36px;
   height: 3px;
   border-radius: 3px;
   background: var(--lime);
}
.chord-family {
   margin-bottom: 0;
   gap: 6px;
}
.chord-family .chord {
   min-width: 43px;
   padding: 8px 10px;
   text-align: center;
   border-color: #cfe6d5;
   background: #e4f4e8;
}
.chord-family .chord:hover {
   background: #cfead6;
}
.slash-fields {
   display: grid;
   grid-template-columns: 1fr 1.1fr 14px 1fr;
   align-items: center;
   gap: 5px;
}
.slash-fields .select-input {
   min-width: 0;
   padding: 7px 5px;
   font-size: 12px;
}
.slash-symbol {
   color: var(--green);
   font-size: 19px;
   font-weight: 900;
   text-align: center;
}
.add-slash {
   width: 100%;
   margin-top: 9px;
}
.slash-chord-bank {
   margin: 10px 0 0;
}
.slash-chord {
   background: #d9f0df !important;
}
.custom-chord-entry {
   display: grid;
   grid-template-columns: minmax(0, 1fr) auto;
   gap: 7px;
   align-items: center;
   margin-top: 10px;
}
.custom-chord-entry .text-input {
   min-width: 0;
   padding: 8px;
   font-size: 12px;
}
.custom-chord-preview {
   min-height: 34px;
   display: flex;
   align-items: center;
}
.custom-chord-hint {
   color: #91a297;
   font-size: 10px;
}
.custom-chord {
   white-space: nowrap;
   background: #d9f0df !important;
}
.nashville-roots {
   display: grid;
   gap: 7px;
}
.nashville-number-row {
   display: grid;
   grid-template-columns: repeat(7, 1fr);
   gap: 5px;
}
.nashville-zero-row {
   grid-template-columns: 38px;
}
.nashville-key,
.nashville-accidental {
   height: 32px;
   border: 1px solid #d6e8da;
   border-radius: 7px;
   background: #fff;
   color: var(--dark-green);
   font: 800 12px inherit;
   cursor: pointer;
}
.nashville-key.active,
.nashville-accidental.active {
   border-color: var(--green);
   background: var(--green);
   color: #fff;
}
.nashville-octave {
   position: relative;
   display: inline-block;
   min-width: 0.72em;
   height: 1.45em;
   line-height: 1.45em;
   vertical-align: baseline;
}
.nashville-octave-dot {
   position: absolute;
   left: 50%;
   color: currentColor;
   font-size: 0.85em;
   font-weight: 900;
   line-height: 1;
   transform: translateX(-50%);
} /*
  High/low Nashville octave markers: bold filled bullet (●) for clear high/low dots
  in ribbon, live preview, and print/PDF.
*/
.nashville-octave-high .nashville-octave-dot,
.nashville-octave-low .nashville-octave-dot {
   color: currentColor;
   font-size: 0.7em;
   font-weight: 800;
   line-height: 1;
   letter-spacing: 0;
   text-shadow:
      0.2px 0 0 currentColor,
      -0.2px 0 0 currentColor;
   -webkit-print-color-adjust: exact;
   print-color-adjust: exact;
}
/* Low-octave dots: original was 100% + 1px; raise only slightly so they stay clear of lyrics without touching the number. */
.nashville-octave-low .nashville-octave-dot {
   top: calc(100% - 2px);
   transform: translateX(-50%);
}
.nashville-octave-high .nashville-octave-dot {
   top: 0.06em;
   bottom: auto;
   transform: translate(-50%, -100%);
}
/*
  ---- Accidental "coret" (slash-through) ------------------------------------
  A half-step alteration is drawn as the degree CROSSED by a slash — the notation
  Indonesian not-angka books use — instead of a ♯/♭ sign in front of it:
    • sharp / naik ½ → the slash leans UP to the right    (/)
    • flat  / turun ½ → the slash leans DOWN to the right (\)
  Two wrappers share the one mark, so a bare degree keeps the markup it always had:
    • .nashville-altered            — a degree with an accidental, no octave dot
    • .nashville-octave.nashville-alt-* — a degree that also carries the dot
  The mark is ABSOLUTELY positioned, so it adds no width to the chord token: the printed
  score's bar wrapping and pagination (measured with getBoundingClientRect in pdf.js) can
  never shift because of it. The digit's optical centre sits exactly at 50% of the wrapper's
  line box (Inter's cap height equals ascent − descent), so one `top: 50%` centres the slash
  in every context: pill, sub-beat, ribbon palette and paper.
*/
.nashville-altered {
   position: relative;
   display: inline-block;
}
.nashville-slash {
   position: absolute;
   z-index: 2;
   top: 50%;
   left: 50%;
   /* Slightly longer than the digit's advance width, so the mark clearly overshoots the
      number (a player must never wonder whether the slash is a decoration). */
   width: 1.15em;
   /* As thick as the score's own 900-weight stems (and never a hairline on small sub-beat
      fonts or phone screens), so the coret reads as decisive without smearing the digit. */
   height: max(0.1em, 1.8px);
   border-radius: 999px;
   background: currentColor;
   transform: translate(-50%, -50%) rotate(-58deg); /* sharp: rises to the right */
   -webkit-print-color-adjust: exact;
   print-color-adjust: exact;
   pointer-events: none;
}
/* Flat: the mirror image — the slash falls to the right (\). */
.nashville-alt-flat .nashville-slash {
   transform: translate(-50%, -50%) rotate(58deg);
}
/*
  The stored ♯/♭ glyph stays in the DOM (so the value is still selectable, searchable and
  read by screen readers via the wrapper's aria-label) but is NOT drawn: on every surface
  the coret carries the meaning, which keeps the dropdown, the palette and the paper
  showing the same thing. To also print/see the glyph, change this to `inline-block`.
*/
.nashville-sign {
   display: none;
}
/* --- Live editor / preview only: neat, precisely-centered octave dots ---------
   Print & PDF keep the original bold "●" glyph rendering above (frozen baseline).
   On screen we (1) shrink-wrap the octave box to the degree so the dot centers
   exactly over the number, and (2) replace the fuzzy text glyph + text-shadow
   hack with a crisp CSS-drawn circle. Scoped html:not(.is-print-layout) so it
   never touches the print octave-dot weight. */
html:not(.is-print-layout) .nashville-octave {
   min-width: 0;
   text-align: center;
}
html:not(.is-print-layout) .nashville-octave-high .nashville-octave-dot,
html:not(.is-print-layout) .nashville-octave-low .nashville-octave-dot {
   width: 0.3em;
   height: 0.3em;
   font-size: inherit; /* size the circle in em relative to the degree, not the glyph */
   line-height: 0;
   background: currentColor;
   border-radius: 50%;
   text-shadow: none; /* drop the glyph-fattening hack */
   text-indent: -9999px; /* push the "●" text glyph out; the CSS circle stands in */
   overflow: hidden;
}
html:not(.is-print-layout) .nashville-octave-high .nashville-octave-dot {
   top: 0;
   bottom: auto;
   transform: translate(-50%, -140%);
}
html:not(.is-print-layout) .nashville-octave-low .nashville-octave-dot {
   top: 100%;
   bottom: auto;
   transform: translate(-50%, 40%);
}
.nashville-accidentals {
   display: flex;
   gap: 6px;
   margin: 9px 0;
}
.nashville-accidental {
   width: 36px;
}
.nashville-family {
   margin-bottom: 0;
}
.nashville-chord {
   min-width: 43px;
   background: #e4f4e8 !important;
}
/* Compact Nashville ribbon: labelled notation rows, modifiers, and a clear selection preview. */
.nashville-builder {
   display: grid;
   grid-template-columns: max-content 1px minmax(420px, 1fr) 150px;
   align-items: stretch;
   gap: 14px;
}
.nashville-degree-panel,
.nashville-variation-panel {
   min-width: 0;
}
.nashville-panel-divider {
   width: 1px;
   background: #dbe8df;
}
.nashville-roots {
   gap: 8px;
}
.nashville-row-block {
   display: grid;
   grid-template-columns: 76px auto;
   align-items: center;
   gap: 8px;
}
.nashville-row-label {
   color: #73877a;
   font-size: 9px;
   font-weight: 800;
   line-height: 1.15;
   white-space: nowrap;
}
.nashville-number-row {
   grid-template-columns: repeat(7, 36px);
   gap: 4px;
}
.nashville-number-row.nashville-zero-row {
   grid-template-columns: 36px;
}
.nashville-key {
   width: 36px;
   height: 34px;
   border-radius: 8px;
}
/*
  Arrangement Tools → Nashville only (lower/upper keys, chord family, selected card).
  Keep live preview + PDF octave-dot weight unchanged (.placed-chord / print rules).
*/
.nashville-builder .nashville-octave-high .nashville-octave-dot,
.nashville-builder .nashville-octave-low .nashville-octave-dot {
   font-size: 0.4em;
   font-weight: 600;
   text-shadow: none;
}
.nashville-accidentals {
   display: inline-flex;
   width: max-content;
   gap: 3px;
   margin: 0 0 8px;
   padding: 3px;
   border: 1px solid #dce9df;
   border-radius: 10px;
   background: #f1f7f3;
}
.nashville-accidental {
   border-color: transparent;
}
.nashville-family {
   max-width: 550px;
   gap: 5px;
}
.nashville-selected-card {
   display: flex;
   min-width: 150px;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   gap: 5px;
   padding: 10px 12px;
   border: 1px solid #d7e8dc;
   border-radius: 10px;
   background: #f5faf6;
   text-align: center;
}
.nashville-selected-card > span {
   color: var(--muted);
   font-size: 9px;
   font-weight: 800;
   letter-spacing: 0.08em;
   text-transform: uppercase;
}
.nashville-selected-card strong {
   min-width: 48px;
   padding: 7px 12px;
   border-radius: 8px;
   background: var(--green);
   color: #fff;
   font-size: 18px;
   line-height: 1;
}
.nashville-selected-card small {
   max-width: 125px;
   color: #819187;
   font-size: 9px;
   line-height: 1.3;
}

/* Keep flat and sharp accidentals visually attached to their chord root. */
.chord-token {
   display: inline-flex;
   align-items: baseline;
   white-space: nowrap;
}
.chord-accidental {
   display: inline-block;
   /* "Inter" first: its accidental @font-face (styles/base/fonts.css) supplies ♭/♯/♮
      with ONE self-hosted glyph on every OS, so the score no longer falls back to
      a platform symbol font (Hiragino on macOS vs Segoe UI Symbol on Windows).
      The serif families remain as a safety net if the webfont ever fails. */
   font-family: Inter, Georgia, "Times New Roman", serif;
   font-size: 1.04em;
   font-weight: 700;
   line-height: 1;
   vertical-align: -0.03em;
}
.chord-flat {
   margin-right: -0.14em;
   margin-left: -0.13em;
}
/* The self-hosted sharp glyph carries more right side-bearing than the old OS
   symbol fonts, so it is pulled in a touch tighter to sit flush on its root. */
.chord-sharp {
   margin-right: -0.11em;
   margin-left: -0.09em;
}

.beat-bank {
   display: flex;
   flex-wrap: wrap;
   gap: 8px;
}
.duration-option {
   display: inline-flex;
   align-items: center;
   gap: 7px;
   min-height: 48px;
   padding: 7px 10px;
   border: 1px dashed #94bda1;
   border-radius: 7px;
   color: var(--dark-green);
   background: #f8fcf9;
   font-size: 12px;
   font-weight: 700;
   cursor: grab;
   user-select: none;
}
.duration-option b {
   min-width: 20px;
   font-size: 17px;
   text-align: center;
}
.duration-option > span {
   display: grid;
   gap: 1px;
}
.duration-option small {
   color: var(--muted);
   font-size: 9px;
   font-weight: 650;
   white-space: nowrap;
}
.preview-actions {
   display: flex;
   gap: 8px;
}
.button-reset {
   color: #9c4742;
   background: #fff2f1;
}
.text-button {
   border: 0;
   background: transparent;
   color: var(--green);
   font: 800 12px inherit;
   cursor: pointer;
   padding: 5px 0;
}
.text-button:hover {
   text-decoration: underline;
}

.bar-grid {
   display: block;
   overflow-x: auto;
   border: 0;
   padding: 0 0 4px;
}
.bar-batch {
   display: flex;
   width: max-content;
   min-width: 100%;
   margin: 18px 0;
   border-top: 1px solid #c9d6cb;
   border-bottom: 1px solid #c9d6cb;
   padding: 13px 0;
   /*
     Live-preview spacing tokens — single source of truth. Every beat slot
     (normal, 1/2, 1/3, 1/4, nested) is exactly --leaf wide with zero gap, so
     the beat pitch is identical whether lyrics are ON or OFF. Never hardcode
     these widths again; always reference the tokens below.
   */
   --leaf: 60px; /* uniform slot width / beat pitch */
   --lyric-h: 32px; /* lyric input + lyric row height */
   --lyric-gap: 8px; /* gap between notation lane and lyric row */
   --lyric-bar-pad: 14px; /* breathing room between barline and beats (lyrics ON only) */
   --chord-above-h: 32px; /* chord-above input + row height (a touch taller than the old 28px) */
   --chord-above-w: 68px; /* uniform chord-cell width (fits Em/C#, F#m7/C#) */
   /*
     NOTE: the beat pitch under a chord row is NOT set here. It is the JS-computed
     `--leaf` (distributeLeafWidth() in events.js, "stretch the line to the score
     width") plus a small `CHORD_ABOVE_LEAF_EXTRA` delta, so a line with the chord
     row keeps the default pitch and simply scrolls horizontally when it no longer
     fits.
   */
   /*
     Gap between the chord-above row and the notation lane. It must clear the
     rhythm beams: the ½ / ⅓ / ¼ marker is lifted above the beat group and, with
     the row switched on, is capped by the `has-chord-above .duration-line` rule
     below so it can never leave the notation lane. This token adds the breathing
     room on top of that guarantee (it was 6px, which tucked the beams into the
     chord input's box).
   */
   --chord-above-gap: 24px;
}
.bar-batch .bar {
   flex: 0 0 auto;
   position: relative;
   min-width: max-content;
   min-height: 60px;
   padding: 12px 0;
   border: 0;
}
/*
  Barline drawn as a zero-layout pseudo-element at the bar's left edge so the
  pitch between the last dot of one bar and the first dot of the next stays
  exactly one --leaf (the line sits centered between them). Every bar gets a
  left line, which also renders the score's leftmost edge on the first bar.
*/
.bar-batch .bar::before {
   content: "";
   position: absolute;
   top: 0;
   bottom: 0;
   left: -0.5px;
   width: 1px;
   background: var(--ink);
   pointer-events: none;
}
/*
  A 1/4 bar IS a beat: the section reads as ONE growing bar, so there is no line
  BETWEEN the beats. The line stays at the two edges of every line of the score
  (the editor draws one line per group of bars): the first bar opens it, the last
  bar closes it. The horizontal rules of the row are not bar lines — they stay.
*/
body[data-meter="1/4"] .bar-batch .bar::before {
   display: none;
}
/* The opening edge of the line. The line sits INSIDE the bar: at the row's left
   edge it would otherwise fall half a pixel OUTSIDE the scrollable grid, where
   the grid's overflow clips it away. */
body[data-meter="1/4"] .bar-batch .bar:first-child::before {
   display: block;
   left: 0;
}
/* The closing edge of the line. The screen has no right bar line of its own
   (only the print layout draws one), so it is introduced here, mirroring the
   left bar line above. It sits half a pixel inside the bar: the row is a hair
   wider than the scrollable grid, so a line flush with the bar's edge would be
   clipped away by the grid's overflow. */
body[data-meter="1/4"] .bar-batch .bar:last-child::after {
   content: "";
   position: absolute;
   top: 0;
   bottom: 0;
   right: 0.5px;
   width: 1px;
   background: var(--ink);
   pointer-events: none;
}
.beat .placed-chord.duration-half::before,
.beat .placed-chord.duration-quarter::before {
   content: "";
   position: absolute;
   left: 2px;
   right: 2px;
   top: -8px;
   height: 2px;
   border-radius: 2px;
   background: currentColor;
}
.beat .placed-chord.duration-quarter::before {
   box-shadow: 0 4px 0 currentColor;
}
.beat-group {
   width: 100%;
   height: 34px;
   position: relative;
   padding-top: 0;
}
.sub-beats {
   display: grid;
   height: 34px;
   gap: 0;
   padding: 0 2px;
}
.duration-half .sub-beats {
   grid-template-columns: repeat(2, minmax(0, 1fr));
}
.duration-quarter .sub-beats {
   grid-template-columns: repeat(4, minmax(0, 1fr));
}
.sub-beat {
   min-width: 0;
   display: grid;
   place-items: center;
   position: relative;
   border-radius: 4px;
   margin: 0 1px;
}
/* .sub-beat .beat-dot styling is defined once in the Chart readability block below. */
.duration-line {
   position: absolute;
   top: 1px;
   left: 7px;
   right: 7px;
   height: 2px;
   border-radius: 2px;
   background: var(--dark-green);
}
.duration-quarter .duration-line {
   box-shadow: 0 4px 0 var(--dark-green);
}
.sub-beat.dragover,
.beat.dragover {
   /*
     Visual-only hover affordance during drag. MUST NOT change height, display,
     or alignment — doing so reflows the beat out from under the cursor, which
     triggers a dragleave/dragenter loop (flicker) and makes the drop fail.
   */
   background: rgba(35, 122, 80, 0.12);
   box-shadow: inset 0 0 0 1px rgba(35, 122, 80, 0.45);
   border-radius: 4px;
}
/* Unified hover drag affordance for empty beats across all rhythm types */
@media (hover: hover) {
   html:not(.is-print-layout) body.is-dragging .beat.drop-target:not(.has-chord),
   html:not(.is-print-layout) body.is-dragging .sub-beat.drop-target:not(.has-chord) {
      background: rgba(35, 122, 80, 0.08);
      box-shadow: inset 0 0 0 1px rgba(35, 122, 80, 0.3);
      border-radius: 4px;
   }
}
.sub-beat .placed-chord {
   position: absolute;
   inset: 0;
   display: grid;
   place-items: center;
   border-radius: 5px;
   background: #dff2e4;
   color: var(--dark-green);
   font:
      900 14px/1 Inter,
      ui-sans-serif,
      sans-serif;
   cursor: pointer;
}
.section-title {
   border: 0;
   padding: 0;
   background: transparent;
   color: var(--green);
   font-weight: 800;
   font-size: 15px;
   letter-spacing: 0.11em;
   cursor: pointer;
}
.section-title:hover {
   text-decoration: underline;
   text-underline-offset: 4px;
}
.section-title-input {
   width: 170px;
   border: 1px solid #71af8a;
   border-radius: 6px;
   padding: 5px 7px;
   color: var(--green);
   background: #fff;
   font-weight: 800;
   font-size: 14px;
   letter-spacing: 0.06em;
   outline: 0;
}

.beat-group {
   width: auto;
   min-width: 34px;
}
.duration-triplet .sub-beats {
   grid-template-columns: repeat(3, minmax(var(--leaf, 60px), max-content));
   gap: 0;
}
.beat.has-chord {
   width: auto;
   min-width: var(--leaf, 60px);
}
.beat.has-chord .placed-chord {
   position: relative;
   inset: auto;
   min-width: 42px;
   padding: 0 10px;
   white-space: nowrap;
}
.duration-half .sub-beats {
   grid-template-columns: repeat(2, max-content);
}
.duration-quarter .sub-beats {
   grid-template-columns: repeat(4, max-content);
}
.sub-beat.has-chord .placed-chord {
   position: relative;
   inset: auto;
   min-width: 32px;
   padding: 0 7px;
   white-space: nowrap;
}
.duration-half .sub-beats {
   grid-template-columns: repeat(2, minmax(var(--leaf, 60px), max-content));
   gap: 0;
}
.duration-quarter .sub-beats {
   grid-template-columns: repeat(4, minmax(var(--leaf, 60px), max-content));
   gap: 0;
}
.sub-beat.has-chord {
   min-width: max-content;
}
.duration-line {
   top: -16px;
   z-index: 1;
   height: 18px;
   background: transparent;
   cursor: pointer;
}
.duration-line::before {
   content: "";
   position: absolute;
   top: 8px;
   right: 0;
   left: 0;
   height: 2px;
   border-radius: 2px;
   background: var(--dark-green);
}
.duration-quarter .duration-line {
   box-shadow: none;
}
.duration-quarter .duration-line::after {
   content: "";
   position: absolute;
   top: 12px;
   right: 0;
   left: 0;
   height: 2px;
   border-radius: 2px;
   background: var(--dark-green);
}

/* A second 1/2 marker splits only one child of the first 1/2 marker. */
.nested-beat-group {
   position: relative;
   display: flex;
   width: max-content;
   height: 34px;
   align-items: center;
}
.nested-sub-beats {
   display: grid;
   width: max-content;
   height: 34px;
   grid-template-columns: repeat(2, minmax(var(--leaf, 60px), max-content));
   gap: 0;
}
.beat-group.has-nested-duration > .duration-line {
   height: 10px;
}
.nested-duration-line {
   position: absolute;
   top: -10px;
   right: 7px;
   left: 7px;
   z-index: 3;
   height: 12px;
   border-radius: 4px;
   cursor: pointer;
}
.nested-duration-line::before {
   content: "";
   position: absolute;
   top: 2px;
   right: 0;
   left: 0;
   height: 2px;
   border-radius: 2px;
   background: var(--dark-green);
}
.quarter-print-line {
   display: none;
}
.duration-line:hover::before,
.nested-duration-line:hover::before {
   background: #0b8a50;
   box-shadow: 0 0 0 3px rgba(31, 112, 74, 0.12);
}
.nested-sub-beat.dragover {
   background: #e3f4e6;
   outline: 1px dashed var(--green);
}
.placed-chord,
.placed-chord .nashville-octave {
   overflow: visible;
}
/*
  High-octave dots sit tight on the degree. Rhythm beams are lifted higher so
  1/2, 1/4, and nested 1/2 never collide with the dot and stay fully visible.
  Score high/low octave dots use a bold filled bullet so they stay readable.
*/
.nashville-octave-high .nashville-octave-dot {
   top: 0.14em;
   bottom: auto;
   transform: translate(-50%, -100%);
}
.placed-chord .nashville-octave-high .nashville-octave-dot,
.sub-beat .placed-chord .nashville-octave-high .nashville-octave-dot,
.placed-chord .nashville-octave-low .nashville-octave-dot,
.sub-beat .placed-chord .nashville-octave-low .nashville-octave-dot {
   z-index: 2;
   color: currentColor;
   font-size: 0.72em;
   font-weight: 800;
   line-height: 1;
   text-shadow:
      0.2px 0 0 currentColor,
      -0.2px 0 0 currentColor;
   -webkit-print-color-adjust: exact;
   print-color-adjust: exact;
}
.placed-chord .nashville-octave-high .nashville-octave-dot,
.sub-beat .placed-chord .nashville-octave-high .nashville-octave-dot {
   top: 0.16em;
   bottom: auto;
   transform: translate(-50%, -100%);
}
/* Score notes: tiny lift from baseline so low-octave dots stay under the number, above lyrics. */
.placed-chord .nashville-octave-low .nashville-octave-dot,
.sub-beat .placed-chord .nashville-octave-low .nashville-octave-dot {
   top: calc(100% - 2px);
   bottom: auto;
   transform: translateX(-50%);
}
/* Keep beams drawable above the chord box (prevent clipping of the double 1/4 bars). */
.beat-column,
.notation-cell,
.beat-group,
.nested-beat-group,
.sub-beats,
.sub-beat {
   overflow: visible;
}
.duration-line,
.nested-duration-line {
   z-index: 5;
}
/* Lift primary beams: first bar ~ -22px, second (1/4) ~ -17px from group top. */
.duration-line {
   top: -28px;
   height: 28px;
}
.duration-line::before {
   top: 6px;
}
.duration-quarter .duration-line::after {
   top: 11px;
}
.beat-group.has-nested-duration > .duration-line {
   height: 16px;
}
/*
  Nested 1/2 beam sits 5px below the primary beam — matching the row spacing of
  the 1/4 (quarter) double beam — instead of dropping to the nested host top.
*/
.nested-duration-line {
   top: -19px;
   /*
     Keep the SAME optical inset as the parent half beam so the two bars of a nested
     1/2 line up on the edge they share — exactly how the 1/4's two bars stack and how
     the printed/PDF beam already looks (see the is-print-layout rules below, which
     drop the .sub-beats padding instead).
     The parent .duration-line sits 7px inside the padding-free .beat-group, while
     .sub-beats carries a 2px horizontal padding, so a nested host hugging one side of
     that grid is 2px further in: 7px − 2px = 5px.
   */
   right: 5px;
   left: 5px;
   height: 14px;
   z-index: 6;
   background: transparent;
}
.nested-duration-line::before {
   content: "";
   display: block;
   position: absolute;
   top: 2px;
   right: 0;
   left: 0;
   height: 2px;
   border-radius: 2px;
   background: var(--dark-green);
}
/*
  Chord Chart WITH the chord row (CHORDS On): keep the whole ½ / ⅓ / ¼ marker
  INSIDE the notation lane. The beams are normally lifted above the beat group
  (top: -28px, visible line ~2px above the lane top) which put the green line and
  its 28px hit area inside the chord input's box. Shifting the marker 8px down —
  primary beam line at -14px, secondary / nested at -9px from the group top, i.e.
  spacing preserved — makes the collision impossible at any zoom, while
  `--chord-above-gap` adds the breathing room above it.

  Scoped to `html:not(.is-print-layout)` on purpose: the PDF / print geometry
  (--print-beam-top) and the on-screen PDF-layout preview are untouched.
*/
html:not(.is-print-layout) .bar.has-chord-above .duration-line {
   top: -20px;
   height: 20px;
}
html:not(.is-print-layout) .bar.has-chord-above .nested-duration-line {
   top: -11px;
}

/*
  Uniform beat pitch in Live Preview: every leaf slot is --leaf wide with zero
  internal gap, and beats pack flush so the pitch between adjacent dots is the
  same whether a beat is whole, 1/2, 1/3, 1/4, or a nested 1/2. Bars grow to
  content width and the score scrolls horizontally instead of squeezing beats.
*/
.bar-batch .bar:not(.has-lyrics) {
   justify-content: flex-start;
   gap: 0;
}

/* Ribbon editor */
body {
   overflow-x: hidden;
}
.ribbon-workspace {
   display: block;
}
.ribbon-workspace .editor-card {
   position: sticky;
   top: 0;
   z-index: 4;
   width: 100%;
   margin: 0 0 24px;
   padding: 12px 20px 14px;
   border-radius: 16px;
   box-shadow: 0 7px 16px rgba(24, 67, 42, 0.1);
}
.metadata-store {
   display: none;
}
.ribbon-workspace .divider {
   margin: 15px 0 12px;
}
.ribbon-tabs {
   display: flex;
   gap: 5px;
   border-bottom: 1px solid var(--line);
}
.ribbon-tab {
   border: 0;
   border-radius: 8px 8px 0 0;
   padding: 9px 15px;
   background: transparent;
   color: var(--muted);
   font: 800 12px inherit;
   cursor: pointer;
}
.ribbon-tab.active {
   background: var(--soft);
   color: var(--dark-green);
   box-shadow: inset 0 -3px 0 var(--lime);
}
.ribbon-panels {
   min-height: 114px;
   padding-top: 12px;
   overflow-x: auto;
}
.ribbon-panel {
   display: none;
}
.ribbon-panel.active {
   display: flex;
   align-items: stretch;
   gap: 14px;
   min-width: max-content;
}
.ribbon-panel .palette-group {
   margin: 0;
   min-width: 250px;
}
.ribbon-panel .palette-divider {
   width: 1px;
   height: auto;
   min-height: 76px;
   margin: 0 1px;
   background: var(--line);
}
.ribbon-panel .palette-divider span {
   width: 3px;
   height: 32px;
   top: 0;
   left: -1px;
}
.ribbon-panel .chord-root-picker {
   grid-template-columns: repeat(6, 33px);
   gap: 5px;
}
.ribbon-panel .chord-root-picker .key {
   width: 33px;
   height: 31px;
}
.ribbon-panel .nashville-builder {
   min-width: 420px;
}
.ribbon-panel .nashville-number-row {
   grid-template-columns: repeat(7, 38px);
}
.ribbon-panel .nashville-number-row.nashville-zero-row {
   grid-template-columns: 38px;
}
.ribbon-panel .field-label {
   margin: 0 0 8px;
}
.preview-stage .preview-card {
   width: 100%;
}
.ribbon-panel .slash-builder {
   display: grid;
   grid-template-columns: max-content max-content max-content;
   grid-template-rows: auto auto;
   align-items: center;
   column-gap: 11px;
   min-width: 720px;
}
.ribbon-panel .slash-builder .palette-label {
   grid-column: 1 / -1;
}
.ribbon-panel .slash-fields {
   grid-column: 1;
}
.ribbon-panel .add-slash {
   grid-column: 2;
   width: auto;
   margin: 0;
   white-space: nowrap;
}
.ribbon-panel .slash-chord-bank {
   grid-column: 3;
   margin: 0;
   min-width: 120px;
}
.preview-editable {
   cursor: pointer;
}
.preview-editable:hover {
   color: var(--green);
}
.preview-editable.is-editing {
   display: block;
}
.preview-inline-input {
   width: min(100%, 520px);
   border: 1px solid #71af8a;
   border-radius: 7px;
   padding: 4px 7px;
   background: #fff;
   color: var(--ink);
   font: inherit;
   outline: 0;
}
.song-meta .preview-inline-input {
   width: 70px;
   padding: 2px 4px;
   text-align: center;
   font-size: 13px;
}
.transpose-control {
   display: inline-flex;
   flex-direction: column;
   align-items: center;
   gap: 4px;
   color: var(--muted);
   font-size: 9px;
   letter-spacing: 0.08em;
   white-space: nowrap;
}
.transpose-buttons {
   display: inline-flex;
   gap: 3px;
}
.transpose-buttons button {
   width: 22px;
   height: 20px;
   border: 1px solid #cfe0d3;
   border-radius: 5px;
   padding: 0;
   background: #f8fcf9;
   color: var(--dark-green);
   font:
      900 15px/1 Inter,
      ui-sans-serif,
      sans-serif;
   cursor: pointer;
}
.transpose-buttons button:hover {
   border-color: #8eb99a;
   background: #e5f4e8;
}
.transpose-buttons button:active {
   transform: translateY(1px);
}

/* Lyrics controls */
.ribbon-panel[data-ribbon-panel="lyrics"].active {
   align-items: center;
}
.lyrics-settings {
   min-width: 440px !important;
   display: flex;
   align-items: center;
   justify-content: space-between;
   gap: 28px;
}
.lyrics-setting-copy {
   max-width: 285px;
   margin: 0;
   color: var(--muted);
   font-size: 11px;
   line-height: 1.4;
}
.switch-control {
   display: inline-flex;
   align-items: center;
   gap: 9px;
   color: var(--dark-green);
   font-size: 12px;
   cursor: pointer;
   white-space: nowrap;
}
.switch-control input {
   position: absolute;
   opacity: 0;
   pointer-events: none;
}
.switch-track {
   width: 42px;
   height: 24px;
   padding: 3px;
   border-radius: 999px;
   background: #cbd9ce;
   transition: 0.2s;
}
.switch-track span {
   display: block;
   width: 18px;
   height: 18px;
   border-radius: 50%;
   background: #fff;
   box-shadow: 0 1px 4px rgba(25, 51, 38, 0.2);
   transition: 0.2s;
}
.switch-control input:checked + .switch-track {
   background: var(--green);
}
.switch-control input:checked + .switch-track span {
   transform: translateX(18px);
}
.switch-control input:focus-visible + .switch-track {
   outline: 3px solid #d9efd6;
   outline-offset: 2px;
}
.lyrics-shortcuts {
   display: flex;
   align-items: center;
   gap: 18px;
   padding: 0 10px;
   color: var(--muted);
   font-size: 11px;
   white-space: nowrap;
}
.lyrics-shortcuts span {
   display: inline-flex;
   align-items: center;
   gap: 5px;
}
.lyrics-shortcuts kbd {
   padding: 2px 5px;
   border: 1px solid #cadacf;
   border-bottom-width: 2px;
   border-radius: 4px;
   background: #fff;
   color: var(--ink);
   font: 700 10px inherit;
}
.auto-syllable {
   display: flex;
   flex-direction: column;
   gap: 9px;
   align-items: stretch;
}
.auto-syllable-input {
   width: 100%;
   min-height: 44px;
   resize: vertical;
   padding: 9px 11px;
   border: 1px solid #cfe4d4;
   border-radius: 10px;
   background: #fff;
   color: var(--ink);
   font: 500 13px/1.4 inherit;
}
.auto-syllable-input:focus-visible {
   outline: 3px solid #d9efd6;
   outline-offset: 1px;
   border-color: var(--green);
}
.auto-syllable-actions {
   display: flex;
   align-items: center;
   gap: 10px;
   flex-wrap: wrap;
}
.auto-syllable-hint {
   color: var(--muted);
   font-size: 11px;
   line-height: 1.3;
}
.section-lyrics-toggle,
.section-chord-above-toggle {
   border: 1px solid #cfe4d4;
   border-radius: 999px;
   padding: 5px 9px;
   background: #fff;
   color: var(--muted);
   font: 800 10px inherit;
   cursor: pointer;
   white-space: nowrap;
}
.section-lyrics-toggle span,
.section-chord-above-toggle span {
   display: inline-grid;
   width: 14px;
   height: 14px;
   margin-right: 3px;
   place-items: center;
   border-radius: 50%;
   background: #e5eee7;
}
.section-lyrics-toggle.active,
.section-chord-above-toggle.active {
   border-color: #b9dfc2;
   background: #edf8f0;
   color: var(--green);
}
.section-lyrics-toggle.active span,
.section-chord-above-toggle.active span {
   background: var(--green);
   color: #fff;
}

/* Live preview: every beat and sub-beat has an equal, readable lyric cell. */
.bar-batch.has-lyrics {
   display: flex;
   width: max-content;
   min-width: 100%;
}
.bar-batch.has-lyrics .bar.has-lyrics {
   flex: 0 0 auto;
   width: max-content;
   min-width: max-content;
   display: grid;
   grid-template-columns: repeat(var(--beats), max-content);
   align-items: start;
   padding-top: 9px;
   padding-bottom: 9px;
   /* Breathing room so beats don't touch the barline in lyrics mode — keeps the
      score readable. The barline pseudo-element sits at the bar's left edge, so
      symmetric left/right padding centers each bar's beats between its lines. */
   padding-left: var(--lyric-bar-pad, 14px);
   padding-right: var(--lyric-bar-pad, 14px);
   /* Uniform beat pitch in lyrics mode too: no inter-column gap so the pitch
      matches the cross-bar pitch (both = one fixed lyric column). */
   column-gap: 0;
}
/*
  Notation lane is taller so numbers/dots/lyrics sit lower.
  Duration beams stay absolute from the lane top and do not move with content.
*/
.beat-column {
   min-width: var(--leaf, 60px);
   display: grid;
   grid-template-rows: 54px;
   align-items: center;
   justify-items: stretch;
}
.beat-column.with-lyrics {
   grid-template-rows: 54px var(--lyric-h, 32px);
   row-gap: var(--lyric-gap, 8px);
}
/* Chord-above (Chord Chart mode): a chord row sits ABOVE the notation lane.
   The chord-above HTML is the FIRST child, so the top grid row holds it. */
.beat-column.with-chord-above {
   grid-template-rows: var(--chord-above-h, 28px) 54px;
   row-gap: var(--chord-above-gap, 6px);
}
.beat-column.with-chord-above.with-lyrics {
   grid-template-rows: var(--chord-above-h, 28px) 54px var(--lyric-h, 32px);
   row-gap: var(--chord-above-gap, 6px) var(--lyric-gap, 8px);
}
.notation-cell {
   min-width: 0;
   height: 54px;
   display: flex;
   align-items: flex-end;
   justify-content: center;
}
.notation-cell > .beat {
   flex: 0 0 auto;
   align-self: flex-end;
   min-height: 34px;
}
.duration-column {
   min-width: max-content;
}
.lyric-editor {
   display: block;
   justify-self: center;
   min-width: 0;
   width: 40px;
}
.lyric-print {
   display: none;
}
.lyric-input {
   width: 100%;
   min-width: 0;
   height: var(--lyric-h, 32px);
   border: 1px solid #d8e7dc;
   border-radius: 7px;
   padding: 5px 6px;
   background: rgba(255, 255, 255, 0.78);
   color: #294235;
   font:
      650 13px/1.2 Inter,
      ui-sans-serif,
      sans-serif;
   text-align: center;
   outline: 0;
   transition:
      border-color 0.15s,
      box-shadow 0.15s,
      background 0.15s;
}
.lyric-input::placeholder {
   color: #a8b7ac;
   font-weight: 500;
}
.lyric-input:hover {
   border-color: #b7d3be;
   background: #fff;
}
.lyric-input:focus {
   position: relative;
   z-index: 2;
   border-color: #64a77b;
   background: #fff;
   box-shadow: 0 0 0 3px rgba(100, 167, 123, 0.14);
}
.sub-lyrics {
   min-width: 0;
   display: grid;
   gap: 6px;
}
.duration-half .sub-lyrics {
   grid-template-columns: repeat(2, minmax(36px, 1fr));
}
.duration-triplet .sub-lyrics {
   grid-template-columns: repeat(3, minmax(34px, 1fr));
}
.duration-quarter .sub-lyrics {
   grid-template-columns: repeat(4, minmax(34px, 1fr));
}
.sub-lyrics .lyric-editor {
   width: 34px;
}

/* ---- Chord-above (Chord Chart mode) ------------------------------------
   A letter-chord row above each number. Mirrors the lyric editor styling but
   sits in the top grid row. Only rendered when the toggle is ON, so it never
   affects layout when OFF.

   Sizing: every cell is the SAME width (--chord-above-w, wide enough for chords
   like `Em/C#`) and the beat pitch under the row widens as a whole
   (--chord-above-leaf), so the two rows read as a tidy grid. */
.chord-above-editor {
   display: block;
   justify-self: center;
   align-self: end;
   min-width: 0;
   width: var(--chord-above-w, 68px);
}
.chord-above-print {
   display: none;
}
.chord-above-input {
   width: 100%;
   min-width: 0;
   height: var(--chord-above-h, 28px);
   border: 1px solid #cfe0d3;
   border-radius: 8px;
   padding: 2px 6px;
   background: #f2faf4; /* the row reads as a soft band above the beats */
   color: #14513a;
   font:
      800 14px/1.15 Inter,
      ui-sans-serif,
      sans-serif;
   letter-spacing: 0.005em;
   text-align: center;
   outline: 0;
   transition:
      border-color 0.15s,
      box-shadow 0.15s,
      background 0.15s,
      color 0.15s;
}
.chord-above-input::placeholder {
   color: #b8c9be;
   font-weight: 600;
}
.chord-above-input:hover {
   border-color: #8eb99a;
   background: #fff;
}
.chord-above-input:focus {
   position: relative;
   z-index: 2;
   border-color: #64a77b;
   background: #fff;
   box-shadow: 0 0 0 3px rgba(100, 167, 123, 0.16);
}
/* A filled cell gets a touch more presence so the harmony line scans quickly. */
.chord-above-editor.has-chord-above .chord-above-input {
   border-color: #a9d2b8;
   background: #e4f5ea;
   color: #0f4a31;
}
.chord-above-editor.has-chord-above .chord-above-input:hover {
   background: #fff;
}
/* Dark theme mirrors the lyric row: a soft dark chip with green text. */
html[data-theme="dark"] .chord-above-input {
   border-color: #343a35;
   background: rgba(10, 10, 10, 0.82);
   color: #8fd8ac;
}
html[data-theme="dark"] .chord-above-input::placeholder {
   color: #55635a;
}
html[data-theme="dark"] .chord-above-input:hover,
html[data-theme="dark"] .chord-above-input:focus {
   border-color: #4e8a67;
   background: #0a0a0a;
}
html[data-theme="dark"] .chord-above-editor.has-chord-above .chord-above-input {
   border-color: #3f6a52;
   background: #14201a;
   color: #a6e3c0;
}
.sub-chord-above {
   min-width: 0;
   display: grid;
   gap: 0;
   grid-template-columns: repeat(var(--lyric-leaves), minmax(var(--leaf, 60px), max-content));
}
.sub-chord-above .chord-above-editor {
   width: var(--chord-above-w, 68px);
}
/* The ½ / ⅓ / ¼ sub-cells share the plain cell's box (same height, anchored to the
   bottom of the chord track), so the gap from the chord row down to the number is
   uniform for every beat type — with or without a rhythm marker. */
.bar.has-chord-above .sub-chord-above .chord-above-editor {
   height: var(--chord-above-h, 32px);
   align-self: end;
}

/*
  A duration marker divides one beat; it must never widen OR narrow that beat.
  Lyrics ON must keep the EXACT same beat pitch as lyrics OFF. Non-lyrics slots
  are `min-width: --leaf` and grow to `max-content` when a chord is wider than
  --leaf, so lyrics slots must do the same — never lock them to --leaf, or a
  wide chord (e.g. Cmaj7#11) would widen the slot only when lyrics are OFF.
*/
.bar.has-lyrics .beat-column:not(.duration-column),
.bar.has-lyrics .sub-beat {
   min-width: var(--leaf, 60px);
   width: max-content;
}
.bar.has-lyrics .duration-column {
   width: max-content;
   min-width: max-content;
}
.bar.has-lyrics .beat-group {
   min-width: 0;
}
.bar.has-lyrics .beat-group,
.bar.has-lyrics .sub-beats,
.bar.has-lyrics .sub-lyrics {
   width: max-content;
}
.bar.has-lyrics .duration-half .sub-beats,
.bar.has-lyrics .duration-triplet .sub-beats,
.bar.has-lyrics .duration-quarter .sub-beats {
   grid-template-columns: repeat(var(--subdivisions), minmax(var(--leaf, 60px), max-content));
   gap: 0;
}
.bar.has-lyrics .beat-group.has-nested-duration > .sub-beats {
   grid-template-columns: repeat(2, max-content);
}
.bar.has-lyrics .duration-half .sub-beats,
.bar.has-lyrics .duration-half .sub-lyrics {
   --subdivisions: 2;
}
.bar.has-lyrics .duration-triplet .sub-beats,
.bar.has-lyrics .duration-triplet .sub-lyrics {
   --subdivisions: 3;
}
.bar.has-lyrics .duration-quarter .sub-beats,
.bar.has-lyrics .duration-quarter .sub-lyrics {
   --subdivisions: 4;
}
.bar.has-lyrics .sub-lyrics {
   grid-template-columns: repeat(var(--lyric-leaves), minmax(var(--leaf, 60px), max-content));
   gap: 0;
}
.bar.has-lyrics .sub-lyrics .lyric-editor {
   width: 40px;
}
/*
  Chord Chart with the chord row ON keeps the JS-distributed beat pitch (see the
  token comment above) — the small extra room per leaf is added by
  distributeLeafWidth(), never subtracted here.
*/

/*
  A chord-row bar keeps the top band of the bar (the absolutely positioned hover
  affordances: ✕ delete bar at top:3px, ⧉ ⎘ tools next to it — 22px tall, so they
  occupy the first 25px) clear, otherwise those buttons sit on top of the chord
  field above the first beat and visually crop it. Screen only: on paper those
  buttons are hidden and the bar uses its own --print-bar-pad-y-* tokens.
*/
html:not(.is-print-layout) .bar.has-chord-above {
   padding-top: 32px;
}
/* Keep beat pitch uniform when chord-above is ON (mirrors .has-lyrics). */
.bar.has-chord-above .beat-column:not(.duration-column),
.bar.has-chord-above .sub-beat {
   min-width: var(--leaf, 60px);
   width: max-content;
}
.bar.has-chord-above .duration-column {
   width: max-content;
   min-width: max-content;
}
.bar.has-chord-above .beat-group {
   min-width: 0;
}
.bar.has-chord-above .beat-group,
.bar.has-chord-above .sub-beats,
.bar.has-chord-above .sub-chord-above {
   width: max-content;
}
.bar.has-chord-above .sub-chord-above {
   grid-template-columns: repeat(var(--lyric-leaves), minmax(var(--leaf, 60px), max-content));
   gap: 0;
}
.bar.has-lyrics .sub-beat.has-chord .placed-chord {
   min-width: 42px;
   padding-right: 2px;
   padding-left: 2px;
}

@media (max-width: 560px) {
   .ribbon-workspace .editor-card {
      position: static;
      padding: 14px;
   }
   .ribbon-setup {
      grid-template-columns: 1fr 1fr;
   }
   .ribbon-setup .two-fields {
      grid-column: span 2;
   }
   .ribbon-tab {
      padding: 9px 10px;
      font-size: 11px;
   }
   .ribbon-panels {
      margin: 0 -2px;
   }
   .ribbon-panel.active {
      gap: 10px;
   }
   .section-tools {
      align-items: flex-end;
      flex-direction: column;
      gap: 2px;
   }
   .preview-actions {
      width: 100%;
   }
   .bar-batch {
      min-width: 520px;
   }
   .lyrics-settings {
      min-width: 390px !important;
   }
   .lyrics-shortcuts {
      gap: 10px;
   }
}

/*
  Taller notation lane: chord content + lyrics sit lower.
  Duration hosts still fill the lane top, so absolute beams stay put.
*/
.beat-group {
   height: 54px;
   padding-top: 0;
   display: flex;
   align-items: stretch;
   overflow: visible;
}
.notation-cell {
   overflow: visible;
   /* Keep beat content on the lower baseline inside the taller cell. */
   align-items: flex-end;
}
.bar-batch .bar {
   min-height: 80px;
   overflow: visible;
}
.beat .placed-chord,
.sub-beat .placed-chord {
   height: 34px;
   min-height: 34px;
   padding-top: 0;
   padding-bottom: 0;
   font:
      900 14px/1 Inter,
      ui-sans-serif,
      sans-serif;
}
/* Full-height host keeps nested/primary beam anchors at the lane top. */
.sub-beats {
   width: 100%;
   height: 100%;
   align-self: stretch;
   align-items: end;
}
.nested-beat-group {
   height: 100%;
   align-self: stretch;
   align-items: flex-end;
}
.nested-sub-beats {
   align-self: flex-end;
   height: 34px;
}
.beat:not(.has-chord),
.sub-beat:not(.has-chord) {
   display: grid;
   place-items: end center;
   min-width: var(--leaf, 60px);
   min-height: 34px;
   height: 100%;
}
.sub-beat {
   align-self: stretch;
   display: grid;
   place-items: end center;
}
.sub-beat.has-chord .placed-chord {
   min-width: 42px;
   padding-right: 10px;
   padding-left: 10px;
}

/* ============================================================
   Chart readability (screen only) — chords lead, empty slots recede.
   1) Chord glyphs grow to 18px and drop the pill box: bold text reads
      like a real chart; a faint tint remains as the click/touch target.
   2) Empty beat dots shrink and fade so they mark the grid quietly.
   3) Small bar numbers give players a shared reference ("from bar 9").
   4) Section titles become true anchors (the old `font: 800 15px inherit`
      shorthand was invalid and silently dropped by browsers).
   5) Dark-mode barlines/dividers get ~15% more contrast.
   ============================================================ */
.beat .placed-chord,
.sub-beat .placed-chord {
   background: transparent;
   font-size: 18px;
   font-weight: 850;
   letter-spacing: 0.01em;
}
.beat.has-chord .placed-chord,
.sub-beat.has-chord .placed-chord {
   background: rgba(35, 122, 80, 0.07);
}
html[data-theme="dark"] .placed-chord,
html[data-theme="dark"] .sub-beat .placed-chord {
   background: transparent;
   color: #d9f5e3;
}
html[data-theme="dark"] .beat.has-chord .placed-chord,
html[data-theme="dark"] .sub-beat.has-chord .placed-chord {
   background: rgba(120, 220, 160, 0.08);
}
/* Sub-beat dots match regular dots exactly — same size, weight, colour.
   This is the single source of truth for every beat dot (fix #5).
   On-screen only: dots are bolder/darker so empty beats read clearly in the
   live editor. The print/PDF dot is overridden separately in the
   html.is-print-layout block, so this does NOT affect the exported PDF. */
.beat-dot,
.sub-beat .beat-dot {
   font-size: 16px;
   font-weight: 700;
   line-height: 1;
   opacity: 0.75;
   color: #6b7d72;
   transform: none;
}
/* ---- Fermata (hold) ------------------------------------------------------
   A fermata glyph drawn just above the chord/number it belongs to. It is a SCORE
   MARK only — the hold itself is a playback feature — so the SAME glyph is shown in
   the live editor, the PDF-layout preview and the exported PDF (the print blocks
   below mirror this rule). */
.beat.has-fermata,
.sub-beat.has-fermata,
.beat-group.has-fermata,
.nested-beat-group.has-fermata {
   position: relative;
}
.beat-fermata {
   position: absolute;
   left: 50%;
   transform: translateX(-50%);
   display: inline-flex;
   color: var(--dark-green);
   line-height: 0;
   pointer-events: none;
   /* Above the rhythm beams (5) and the chord-above cells (chords+), so the mark
      is never hidden. */
   z-index: 6;
   /* Anchor just above the OWNER's top. A ½/⅓/¼ sub-beat and a subdivided group fill
      the whole notation lane, so their fermata lifts to the lane's top — the
      "rhythm ½" height. Kept owner-relative so it always tracks the lane height. */
   bottom: calc(100% + 1px);
   top: auto;
}
/*
   A nested ½ leaf is a SHORT box hugging the lane bottom (it does NOT fill the
   lane), so the owner-relative anchor above would drop its fermata well BELOW a ½
   sub-beat's. Measure from the LANE height instead (54px = the .notation-cell
   height) so a nested ½ fermata sits at EXACTLY the same height as a rhythm-½ one.
*/
.nested-sub-beat.has-fermata > .beat-fermata {
   bottom: calc(54px + 1px);
}
/*
   A section that shows its chord row (Chords+) AND carries a fermata needs extra
   clearance above the notation lane: a rhythm-½ fermata lifts ~19px above the lane
   top, which would otherwise collide with the chord row. The extra gap is uniform
   across the section, so barlines and beats stay aligned.
*/
.preview-section.has-fermata .beat-column.with-chord-above {
   row-gap: calc(var(--chord-above-gap, 24px) + 16px);
}
.beat-fermata svg {
   display: block;
   width: 20px;
   height: 18px;
}
.beat-fermata svg path {
   /* Bolder than the glyph's built-in 2 — a clear, heavy fermata on screen. */
   stroke-width: 2.7;
}
html[data-theme="dark"] .beat-fermata {
   color: #cdeedb;
}
.bar-num {
   position: absolute;
   top: 2px;
   left: 6px;
   z-index: 1;
   font-size: 10px;
   font-weight: 600;
   line-height: 1;
   color: var(--ink);
   opacity: 0.34;
   pointer-events: none;
   user-select: none;
}
/* 1/4 has no bars to number (the section is one growing bar), so the faint editor
   counter stays out of it — the same reason the printed stamp is hidden there. */
body[data-meter="1/4"] .bar-batch .bar .bar-num {
   display: none;
}
/* .section-title base styles live in the section-heading block above (~line 546). */
html[data-theme="dark"] .bar-batch .bar::before {
   background: rgba(255, 255, 255, 0.28);
}
/* The closing edge of a 1/4 line (screen) follows the same ink as the left one. */
html[data-theme="dark"] body[data-meter="1/4"] .bar-batch .bar:last-child::after {
   background: rgba(255, 255, 255, 0.28);
}
html[data-theme="dark"] .bar-batch {
   border-color: #4a524b;
}

/* ============================================================
   Structure & Polish — section chips, gaps, accent, animations.
   9) Section color chips: a small colored dot left of the title
      auto-detects section type so Verse/Chorus/Bridge etc.
      read at a glance.
   10) Section gaps + is-active accent: active section gets a soft
       inset glow instead of just a left bar; section spacing uses
       a consistent 32px rhythm.
   11) Chord place/remove animations: a subtle scale-pop on drop
       puts motion behind every edit without being distracting.
   ============================================================ */
.section-chip {
   display: inline-block;
   width: 8px;
   height: 8px;
   border-radius: 50%;
   margin-right: 7px;
   vertical-align: middle;
   transform: translateY(-1px);
}
/* --- chip colours per section type --- */
.sec-verse .section-chip {
   background: #4a9d6e;
}
.sec-chorus .section-chip {
   background: #d4973b;
}
.sec-bridge .section-chip {
   background: #b876c4;
}
.sec-intro .section-chip {
   background: #5b8cb8;
}
.sec-outro .section-chip {
   background: #9b6f61;
}
.sec-prechorus .section-chip {
   background: #7fad5e;
}
.sec-interlude .section-chip {
   background: #708090;
}
.sec-default .section-chip {
   background: var(--muted, #718176);
}

html[data-theme="dark"] .sec-verse .section-chip {
   background: #6cc493;
}
html[data-theme="dark"] .sec-chorus .section-chip {
   background: #e8b85a;
}
html[data-theme="dark"] .sec-bridge .section-chip {
   background: #cf96e0;
}
html[data-theme="dark"] .sec-intro .section-chip {
   background: #7aaed8;
}
html[data-theme="dark"] .sec-outro .section-chip {
   background: #b89382;
}
html[data-theme="dark"] .sec-prechorus .section-chip {
   background: #9cc57a;
}
html[data-theme="dark"] .sec-interlude .section-chip {
   background: #9ba8b3;
}
html[data-theme="dark"] .sec-default .section-chip {
   background: #8a978c;
}

/* --- section gaps: consistent 32px spacing rhythm --- */
.preview-section {
   padding: 0 0 32px;
   margin-bottom: 32px;
}

/* --- is-active accent: soft inset glow replaces the thick left bar --- */
.preview-section.is-active {
   position: relative;
   background: rgba(239, 247, 241, 0.5);
   border-radius: 12px;
   margin-left: -12px;
   margin-right: -12px;
   padding-left: 12px;
   padding-right: 12px;
}
.preview-section.is-active::before {
   content: "";
   position: absolute;
   top: 0;
   bottom: 0;
   left: 0;
   width: 3px;
   border-radius: 3px;
   background: var(--lime);
}
html[data-theme="dark"] .preview-section.is-active {
   background: rgba(35, 60, 45, 0.35);
}
html[data-theme="dark"] .preview-section.is-active::before {
   background: #6cc493;
}

/* --- chord place animation (drop-success already exists) --- */
/* --- chord remove: quick shrink out --- */
.placed-chord {
   transition:
      transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1),
      opacity 0.18s ease,
      filter 0.18s ease;
}
.placed-chord.chord-leaving {
   transform: scale(0.3);
   opacity: 0;
   transition:
      transform 0.12s ease-in,
      opacity 0.12s ease-in;
}

/* --- chord arrive: overbounce pop (reuse drop-success) --- */
.drop-success .placed-chord {
   animation: chordPop 0.38s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes chordPop {
   0% {
      transform: scale(0.4);
      opacity: 0.4;
   }
   60% {
      transform: scale(1.12);
      opacity: 1;
   }
   100% {
      transform: scale(1);
      opacity: 1;
   }
}

/* ============================================================
   Edit affordance (screen only, pointer devices only) — the sheet
   stays clean at rest; editing hints appear on hover.
   6) Empty beat hover: "+" hint + soft ring says "drop a chord here".
   7) Placed chord hover: tint strengthens to hint "tap to remove".
   8) Bar hover: faint wash + bar number brightens; the existing
      delete-× reveal (styles/layout/shell.css) rides on the same hover.
   9) Active section tools rest at 55% opacity, full on hover, so a
      single-section sheet still reads like a finished chart.
   Touch devices (hover: none) keep everything always visible.
   ============================================================ */
@media (hover: hover) {
   html:not(.is-print-layout) body:not(.is-dragging) .beat.drop-target:not(.has-chord):hover,
   html:not(.is-print-layout) body:not(.is-dragging) .sub-beat.drop-target:not(.has-chord):hover {
      cursor: text;
   }
   html:not(.is-print-layout) body:not(.is-dragging) .beat.drop-target:not(.has-chord):hover .beat-dot,
   html:not(.is-print-layout) body:not(.is-dragging) .sub-beat.drop-target:not(.has-chord):hover .beat-dot {
      opacity: 0;
   }
   /* Hover affordance + open-editor highlight share ONE inset box so the hover
      preview and the clicked/active target are exactly the same size. The box
      sits a few px inside the beat cell (slightly smaller than the full cell)
      and scales with every beat width — normal, ½, ⅓, ¼ — instead of the old
      fixed 84px chip that overflowed narrow cells. */
   html:not(.is-print-layout)
      body:not(.is-dragging)
      .beat.drop-target:not(.has-chord):not(.chord-editing):hover::before,
   html:not(.is-print-layout)
      body:not(.is-dragging)
      .sub-beat.drop-target:not(.has-chord):not(.chord-editing):hover::before {
      content: "";
      position: absolute;
      z-index: 5;
      inset: 21px 16px 3px;
      border: 1.5px dashed rgba(35, 122, 80, 0.5);
      border-radius: 5px;
      background: rgba(35, 122, 80, 0.06);
      pointer-events: none;
   }
   html:not(.is-print-layout) body:not(.is-dragging) .beat.drop-target:not(.has-chord):not(.chord-editing):hover::after,
   html:not(.is-print-layout)
      body:not(.is-dragging)
      .sub-beat.drop-target:not(.has-chord):not(.chord-editing):hover::after {
      content: "+";
      position: absolute;
      z-index: 6;
      inset: 21px 16px 3px;
      display: grid;
      place-items: center;
      font:
         700 15px/1 Inter,
         ui-sans-serif,
         sans-serif;
      color: var(--green);
      pointer-events: none;
   }
   html[data-theme="dark"]:not(.is-print-layout)
      body:not(.is-dragging)
      .beat.drop-target:not(.has-chord):not(.chord-editing):hover::before,
   html[data-theme="dark"]:not(.is-print-layout)
      body:not(.is-dragging)
      .sub-beat.drop-target:not(.has-chord):not(.chord-editing):hover::before {
      border-color: rgba(120, 220, 160, 0.5);
      background: rgba(120, 220, 160, 0.08);
   }
   html[data-theme="dark"]:not(.is-print-layout)
      body:not(.is-dragging)
      .beat.drop-target:not(.has-chord):not(.chord-editing):hover::after,
   html[data-theme="dark"]:not(.is-print-layout)
      body:not(.is-dragging)
      .sub-beat.drop-target:not(.has-chord):not(.chord-editing):hover::after {
      color: #8fd4a8;
   }

   html:not(.is-print-layout) .beat.has-chord .placed-chord:hover,
   html:not(.is-print-layout) .sub-beat.has-chord .placed-chord:hover {
      background: rgba(35, 122, 80, 0.14);
   }
   html[data-theme="dark"]:not(.is-print-layout) .beat.has-chord .placed-chord:hover,
   html[data-theme="dark"]:not(.is-print-layout) .sub-beat.has-chord .placed-chord:hover {
      background: rgba(120, 220, 160, 0.16);
   }

   html:not(.is-print-layout) .bar-batch .bar:hover {
      background: rgba(35, 122, 80, 0.025);
   }
   html:not(.is-print-layout) .bar-batch .bar:hover .bar-num {
      opacity: 0.7;
   }
   html[data-theme="dark"]:not(.is-print-layout) .bar-batch .bar:hover {
      background: rgba(255, 255, 255, 0.03);
   }

   .preview-section.is-active:not(:hover):not(:focus-within) .section-tools {
      opacity: 0.55;
   }
   .preview-section.is-active:hover .section-tools,
   .preview-section.is-active:focus-within .section-tools {
      opacity: 1;
   }
}

/* ============================================================
   First-time-friendly editing (screen only).
   A) × badge on placed chords: hover (or always, on touch) shows a
      small red × at the top-right corner — an unmistakable delete
      control that always deletes, even while a palette item is armed.
   B) "Placing" banner above the canvas: whenever a palette item is
      selected, a pill shows what is armed and how to cancel, so the
      place/replace vs delete modes are never invisible.
   ============================================================ */
.chord-remove {
   position: absolute;
   top: -7px;
   right: -7px;
   z-index: 4;
   width: 16px;
   height: 16px;
   display: grid;
   place-items: center;
   border: 1px solid #eccfcb;
   border-radius: 50%;
   background: #fff;
   color: #b3453e;
   font:
      800 11px/1 Inter,
      ui-sans-serif,
      sans-serif;
   cursor: pointer;
   opacity: 0;
   transform: scale(0.8);
   transition:
      opacity 0.13s ease,
      transform 0.13s ease,
      background 0.13s ease;
}
.placed-chord:hover .chord-remove,
.placed-chord:focus-visible .chord-remove {
   opacity: 1;
   transform: scale(1);
}
.chord-remove:hover {
   background: #fdeeec;
}
@media (hover: none) {
   .chord-remove {
      opacity: 0.85;
      transform: scale(1);
   }
}
html[data-theme="dark"] .chord-remove {
   border-color: #6e4a46;
   background: #2a2320;
   color: #f0a49c;
}
html[data-theme="dark"] .chord-remove:hover {
   background: #3a2b28;
}
html.is-print-layout .chord-remove {
   display: none !important;
}

.placing-banner {
   display: flex;
   align-items: center;
   justify-content: space-between;
   gap: 10px;
   margin: 0 0 8px;
   padding: 7px 12px;
   border: 1px solid #cfe6d6;
   border-radius: 10px;
   background: #eef8f1;
   color: var(--dark-green, #125838);
   font-size: 13px;
}
.placing-banner[hidden] {
   display: none;
}
.placing-banner-text b {
   font-weight: 800;
}
.placing-cancel {
   flex: none;
   border: 1px solid #d6e7db;
   border-radius: 8px;
   padding: 4px 10px;
   background: #fff;
   color: var(--dark-green, #125838);
   font:
      700 12px/1.2 Inter,
      ui-sans-serif,
      sans-serif;
   cursor: pointer;
   transition: background 0.15s ease;
}
.placing-cancel:hover {
   background: #f2faf4;
}
html[data-theme="dark"] .placing-banner {
   border-color: #3f5a49;
   background: #1d2b22;
   color: #bfe9cd;
}
html[data-theme="dark"] .placing-cancel {
   border-color: #46614f;
   background: #24352a;
   color: #bfe9cd;
}
html[data-theme="dark"] .placing-cancel:hover {
   background: #2c4133;
}

/*
  Print / PDF score geometry.
  Tune these tokens to improvise export layout without hunting selectors:
  --print-slot            minimum width of one leaf beat/sub-beat
  --print-sub-gap         gap between subdivisions under a duration marker
  --print-duration-stroke thickness of 1/2, 1/3, 1/4 marker lines
  --print-duration-inset  optical inset of marker lines from group edges
  --print-chord-size      chord glyph size
  --print-lyric-size      lyric text size
*/
/* ============================================================================
   PRINT / PDF SPACING TOKENS — SINGLE SOURCE OF TRUTH
   ----------------------------------------------------------------------------
   Edit these values in ONE place to tune PDF/print geometry. They are consumed
   by BOTH print contexts:
     • @media print          — the real print/PDF output (Cmd+P, Export PDF)
     • html.is-print-layout   — the on-screen "PDF layout" preview
   Both contexts are active simultaneously during printing (see the beforeprint
   listener in src/events.js). Because these are custom properties, defining
   them unconditionally is inert until referenced by a print-scoped rule, so it
   does NOT affect the live editor layout.
   ============================================================================ */
:root {
   /* Content-hugging beats: empty slots stay slim while chords grow to
      max-content. Slightly widened for clearer beat separation while still
      hugging content (empty beats stay slim, chords grow to max-content). */
   --print-slot: 6.2mm;
   --print-sub-gap: 0.9mm;
   --print-duration-stroke: 0.38mm;
   /* Inset beam ends so adjacent subdivided beats show a visible break
      between their lines (with zero sub-gap they would otherwise merge). */
   --print-duration-inset: 1.2mm;
   --print-bar-pad-x: 2.6mm;
   --print-bar-pad-y-top: 2mm;
   --print-bar-pad-y-bottom: 1mm;
   /* Extra horizontal breathing room between the barline and the beats when
      lyrics are printed, so the sung chart is easier to read. Matches the
      no-lyric bar padding (--print-bar-pad-x) so spacing is uniform. */
   --print-bar-pad-x-lyrics: 2.6mm;
   /* Horizontal inset of a printed ROW. 0 by default: every bar carries its own
      side padding (--print-bar-pad-x). The 1/4 scope hands that inset to the row
      instead, so the beats of the growing bar sit at the normal beat pitch. */
   --print-row-pad-x: 0mm;
   /* Bar numbers (PDF options → Bars) in two densities:
      • "line"  (default) — ONE big number per printed line, above that line's first barline.
      • "every"           — a small number above every barline (dense rehearsal reference).
      Only "line"/"every" draw anything; "off" keeps every token below at its 0mm default, so the
      base bar geometry stays byte-identical. The band tokens reserve room at the top of a bar so
      a digit can never land on the notation (see the html[data-pdf-bar-numbers] rules).

      Tuning the stamp's position: `--print-bar-num-top` moves it DOWN from the bar's top edge and
      `--print-bar-num-inset` moves it RIGHT from the barline. The band below grows/shrinks with
      both, and the trailing constant is the clearance kept between the digit and the notation
      (the digit's box ends `+ 0.5mm` above the first line of music). */
   --print-bar-num-size: 3mm;
   --print-bar-num-size-every: 2.6mm;
   --print-bar-num-inset: 1.3mm;
   --print-bar-num-top: 3.5mm;
   --print-bar-num-weight: 800;
   --print-bar-num-opacity: 1;
   --print-bar-num-row: calc(var(--print-bar-num-size) + var(--print-bar-num-top) + 0.5mm);
   --print-bar-num-row-every: calc(var(--print-bar-num-size-every) + var(--print-bar-num-top) + 0.5mm);
   --print-bar-num-extra-top: 0mm;
   --print-bar-num-extra-bottom: 0mm;
   /* Stamp ink is PINNED to the paper colour instead of var(--ink): the dialog's
      live preview is always a white sheet, but --ink flips to near-white
      (#f1f4f2) in dark mode, which would make the pane promise a stamp the PDF
      never draws. Matches the ink of the on-screen PDF-layout preview card. */
   --print-bar-num-ink: #173a28;
   --print-chord-size: 4.3mm;
   /* The chord row ABOVE the numbers floats in the bar's top band, so it prints a
      shade smaller than the main chords (and scales with the same PDF slider). */
   --print-chord-above-size: calc(var(--print-chord-size) * 0.9);
   --print-chord-above-row: calc(var(--print-chord-above-size) + 0.2mm);
   /* Extra space above a printed line that shows the chord row, so the floating row
      never crowds the chords of the line above. Single knob: raise/lower this value
      (it only applies to sections that actually show the row). */
   --print-chord-above-line-gap: 6mm;
   --print-lyric-size: 3.2mm;
   /* Extra headroom so raised beams + high-octave dots never collide/clip. */
   --print-notation-h: 11.5mm;
   --print-chord-h: 8.2mm;
   --print-beam-top: 0.7mm;
   --print-beam-gap: 0.75mm;
   /* Keep 42px so print barlines stay visually stable and regression-safe. */
   --print-barline-h: 42px;
}

/* ---------------------------------------------------------------------------
   1/4 — ONE GROWING BAR (no bar line).
   A 1/4 bar IS a beat, so the score is drawn as a single bar that keeps growing
   with every added beat:
     • the vertical bar line is dropped — in the live preview, in @media print
       AND in html.is-print-layout (the three scopes must stay identical);
     • the bar's own side padding is handed to the ROW, so the beats of the
       growing bar sit at the same pitch as the beats inside any other bar;
     • the bar numbers stay out: there is no bar line to stamp them on.
   These tokens are inert on screen (print tokens are only read by print-scoped
   rules) and they only ever bite for `data-meter="1/4"`.
   --------------------------------------------------------------------------- */
body[data-meter="1/4"] #previewCard {
   --print-bar-pad-x: 0mm;
   --print-bar-pad-x-lyrics: 0mm;
   --print-row-pad-x: 2.6mm;
}

