feat: badge print — two-line name toggle + leading-none tightening + calibration SVG

- Add name_two_lines toggle (default: true) — uses CSS horizontal padding scaled by
  character count to coax short names (e.g. "Scott Idem") into a natural two-line wrap
  without a hard <br>; three tiers: ≤12 chars (18%), ≤20 (8%), ≤28 (2%), >28 no pad
- Inner <div> (block element) used inside Element_fit_text for class: directives —
  Svelte scoped CSS requires static class names in the template; dynamic strings and
  class: on component elements both fail to match scoped CSS rules
- Add leading-none to all four Element_fit_text fields (name, title, affiliations,
  location) — line-height must be set at the wrapper div level where fit_text measures
  scrollHeight, otherwise the binary-search scaler returns inflated sizes
- name_two_lines state persisted to localStorage (ae_badge_print_tweaks key) alongside
  existing print_offset, hide_chrome, and banner_full_width tweaks
- Rewrite badge_header_calibration.svg as a precise SVG ruler with labeled tick marks
  (major at 1in intervals, minor at 0.25in) for accurate physical print calibration
- Gate debug outline CSS on html.debug_outlines class (set by controls panel) so
  outlines never appear in normal print mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-03-19 17:55:49 -04:00
parent 621a637b85
commit fdd8691e2e
5 changed files with 272 additions and 32 deletions

View File

@@ -107,9 +107,10 @@
outline: 3px dashed purple !important;
outline-offset: -12px !important;
}
/* Cyan = the actual badge — should be dead-center on page */
/* Red = the actual badge — should be dead-center on page.
Thick + high-contrast so misalignment vs. physical card stock is obvious. */
html.debug_outlines .event_badge_wrapper {
outline: 3px solid cyan !important;
outline-offset: 2px !important;
outline: 6px solid red !important;
outline-offset: 3px !important;
}
}

View File

@@ -1,6 +1,168 @@
<svg width="332" height="72" viewBox="0 0 332 72" xmlns="http://www.w3.org/2000/svg">
<rect width="332" height="72" fill="#f8fafc" stroke="#cbd5e1" stroke-width="1" />
<rect x="53" y="24" width="48" height="12" rx="4" fill="#ef4444" fill-opacity="0.4" stroke="#b91c1c" />
<rect x="231" y="24" width="48" height="12" rx="4" fill="#ef4444" fill-opacity="0.4" stroke="#b91c1c" />
<text x="166" y="42" font-family="monospace" font-size="10" fill="#ef4444" text-anchor="middle">CLIP SLOT DANGER ZONE</text>
<!--
Badge Header Calibration Strip — 88mm × 19mm (badge width × header slot height)
Scale: 332 SVG units = 88mm physical → 1mm = 3.7727px
332 SVG units = 3.464in physical → 1in = 95.85px
Use with banner_full_width=ON and debug print mode to verify horizontal alignment.
Top ruler: millimeters | Bottom ruler: inches
Red zones: lanyard clip slot danger areas
-->
<!-- Background -->
<rect width="332" height="72" fill="#f8fafc"/>
<!-- ════════════════════════════════════════════════════ -->
<!-- TOP RULER — millimeters (1mm = 3.7727px) -->
<!-- ════════════════════════════════════════════════════ -->
<!-- Faint separator between ruler and middle zone -->
<line x1="0" y1="17" x2="332" y2="17" stroke="#e2e8f0" stroke-width="0.5"/>
<!-- Unit label -->
<text x="328" y="8" font-family="sans-serif" font-size="5.5" fill="#94a3b8" font-style="italic" text-anchor="end">mm</text>
<!-- 1mm minor ticks (height=3) — all positions not on 5mm multiples -->
<line x1="3.77" y1="0" x2="3.77" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="7.55" y1="0" x2="7.55" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="11.32" y1="0" x2="11.32" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="15.09" y1="0" x2="15.09" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="22.64" y1="0" x2="22.64" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="26.41" y1="0" x2="26.41" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="30.18" y1="0" x2="30.18" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="33.95" y1="0" x2="33.95" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="41.50" y1="0" x2="41.50" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="45.27" y1="0" x2="45.27" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="49.05" y1="0" x2="49.05" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="52.82" y1="0" x2="52.82" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="60.36" y1="0" x2="60.36" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="64.14" y1="0" x2="64.14" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="67.91" y1="0" x2="67.91" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="71.68" y1="0" x2="71.68" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="79.23" y1="0" x2="79.23" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="83.00" y1="0" x2="83.00" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="86.77" y1="0" x2="86.77" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="90.55" y1="0" x2="90.55" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="98.09" y1="0" x2="98.09" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="101.86" y1="0" x2="101.86" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="105.64" y1="0" x2="105.64" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="109.41" y1="0" x2="109.41" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="116.95" y1="0" x2="116.95" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="120.73" y1="0" x2="120.73" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="124.50" y1="0" x2="124.50" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="128.27" y1="0" x2="128.27" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="135.82" y1="0" x2="135.82" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="139.59" y1="0" x2="139.59" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="143.36" y1="0" x2="143.36" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="147.14" y1="0" x2="147.14" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="154.68" y1="0" x2="154.68" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="158.45" y1="0" x2="158.45" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="162.23" y1="0" x2="162.23" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="166.00" y1="0" x2="166.00" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="173.55" y1="0" x2="173.55" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="177.32" y1="0" x2="177.32" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="181.09" y1="0" x2="181.09" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="184.86" y1="0" x2="184.86" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="192.41" y1="0" x2="192.41" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="196.18" y1="0" x2="196.18" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="199.95" y1="0" x2="199.95" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="203.73" y1="0" x2="203.73" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="211.27" y1="0" x2="211.27" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="215.05" y1="0" x2="215.05" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="218.82" y1="0" x2="218.82" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="222.59" y1="0" x2="222.59" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="230.14" y1="0" x2="230.14" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="233.91" y1="0" x2="233.91" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="237.68" y1="0" x2="237.68" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="241.45" y1="0" x2="241.45" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="249.00" y1="0" x2="249.00" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="252.77" y1="0" x2="252.77" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="256.55" y1="0" x2="256.55" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="260.32" y1="0" x2="260.32" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="267.86" y1="0" x2="267.86" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="271.64" y1="0" x2="271.64" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="275.41" y1="0" x2="275.41" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="279.18" y1="0" x2="279.18" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="286.73" y1="0" x2="286.73" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="290.50" y1="0" x2="290.50" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="294.27" y1="0" x2="294.27" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="298.05" y1="0" x2="298.05" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="305.59" y1="0" x2="305.59" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="309.36" y1="0" x2="309.36" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="313.14" y1="0" x2="313.14" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="316.91" y1="0" x2="316.91" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="324.45" y1="0" x2="324.45" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="328.23" y1="0" x2="328.23" y2="3" stroke="#94a3b8" stroke-width="0.75"/>
<!-- 5mm medium ticks (height=7) — at 5,15,25,...,85mm -->
<line x1="18.86" y1="0" x2="18.86" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="56.59" y1="0" x2="56.59" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="94.32" y1="0" x2="94.32" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="132.05" y1="0" x2="132.05" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="169.77" y1="0" x2="169.77" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="207.50" y1="0" x2="207.50" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="245.23" y1="0" x2="245.23" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="282.95" y1="0" x2="282.95" y2="7" stroke="#64748b" stroke-width="1"/>
<line x1="320.68" y1="0" x2="320.68" y2="7" stroke="#64748b" stroke-width="1"/>
<!-- 10mm major ticks (height=13) with labels — at 10,20,...,80mm -->
<line x1="37.73" y1="0" x2="37.73" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="37.73" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">10</text>
<line x1="75.45" y1="0" x2="75.45" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="75.45" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">20</text>
<line x1="113.18" y1="0" x2="113.18" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="113.18" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">30</text>
<line x1="150.91" y1="0" x2="150.91" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="150.91" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">40</text>
<line x1="188.64" y1="0" x2="188.64" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="188.64" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">50</text>
<line x1="226.36" y1="0" x2="226.36" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="226.36" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">60</text>
<line x1="264.09" y1="0" x2="264.09" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="264.09" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">70</text>
<line x1="301.82" y1="0" x2="301.82" y2="13" stroke="#334155" stroke-width="1.25"/>
<text x="301.82" y="12" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">80</text>
<!-- ════════════════════════════════════════════════════ -->
<!-- BOTTOM RULER — inches (1in = 95.85px) -->
<!-- ════════════════════════════════════════════════════ -->
<!-- Faint separator -->
<line x1="0" y1="55" x2="332" y2="55" stroke="#e2e8f0" stroke-width="0.5"/>
<!-- Unit label -->
<text x="328" y="65" font-family="sans-serif" font-size="5.5" fill="#94a3b8" font-style="italic" text-anchor="end">in</text>
<!-- 0.25in ticks (height=5) — at 0.25, 0.75, 1.25, 1.75, 2.25, 2.75, 3.25 in -->
<line x1="23.96" y1="72" x2="23.96" y2="67" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="71.89" y1="72" x2="71.89" y2="67" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="119.82" y1="72" x2="119.82" y2="67" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="167.74" y1="72" x2="167.74" y2="67" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="215.67" y1="72" x2="215.67" y2="67" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="263.60" y1="72" x2="263.60" y2="67" stroke="#94a3b8" stroke-width="0.75"/>
<line x1="311.53" y1="72" x2="311.53" y2="67" stroke="#94a3b8" stroke-width="0.75"/>
<!-- 0.5in ticks (height=9) — at 0.5, 1.5, 2.5 in -->
<line x1="47.93" y1="72" x2="47.93" y2="63" stroke="#64748b" stroke-width="1"/>
<line x1="143.78" y1="72" x2="143.78" y2="63" stroke="#64748b" stroke-width="1"/>
<line x1="239.63" y1="72" x2="239.63" y2="63" stroke="#64748b" stroke-width="1"/>
<!-- 1in major ticks (height=14) with labels — at 1, 2, 3 in -->
<line x1="95.85" y1="72" x2="95.85" y2="58" stroke="#334155" stroke-width="1.25"/>
<text x="95.85" y="64" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">1</text>
<line x1="191.71" y1="72" x2="191.71" y2="58" stroke="#334155" stroke-width="1.25"/>
<text x="191.71" y="64" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">2</text>
<line x1="287.56" y1="72" x2="287.56" y2="58" stroke="#334155" stroke-width="1.25"/>
<text x="287.56" y="64" font-family="sans-serif" font-size="6" fill="#475569" text-anchor="middle">3</text>
<!-- ════════════════════════════════════════════════════ -->
<!-- CLIP SLOT DANGER ZONE markers -->
<!-- Positions retained from original calibration file -->
<!-- ════════════════════════════════════════════════════ -->
<rect x="53" y="22" width="48" height="11" rx="3" fill="#ef4444" fill-opacity="0.35" stroke="#b91c1c" stroke-width="0.75"/>
<rect x="231" y="22" width="48" height="11" rx="3" fill="#ef4444" fill-opacity="0.35" stroke="#b91c1c" stroke-width="0.75"/>
<text x="166" y="39" font-family="monospace" font-size="8.5" fill="#ef4444" text-anchor="middle">CLIP SLOT DANGER ZONE</text>
<!-- 88mm edge tick (right boundary reference) -->
<line x1="330.5" y1="0" x2="330.5" y2="5" stroke="#334155" stroke-width="1.25"/>
<line x1="330.5" y1="72" x2="330.5" y2="67" stroke="#334155" stroke-width="1.25"/>
<!-- ════════════════════════════════════════════════════ -->
<!-- BORDER — drawn last, sits on top of rulers -->
<!-- ════════════════════════════════════════════════════ -->
<rect x="1.5" y="1.5" width="329" height="69" fill="none" stroke="#64748b" stroke-width="3"/>
</svg>

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 13 KiB