Skip to main content

max / makenotwork

server: the pricing calculator is described, and owns its document `1e35bc8a`. `/pricing` is served from a description (`src/quasi/pricing.rs`). `templates/pages/pricing.html`, `templates/partials/fee_calculator.html`, the `<mnw-price-mode>` custom element and its island go with it, along with 278 lines of the stylesheet that existed to draw them. The region consult says the whole of the shipped `hx-trigger` block in one clause: the calculator names the route, the 300ms wait and the panel the answer lands in, and the dials ride along by containment, so nothing names a dial. Five `from:#id` clauses and five ids listed again to be sent are gone. The mode is a dial now, which is what deletes the element. It swapped two `data-price-*` values on four radios and re-fired the recompute, so the browser held a price the server also knew; the tier radio sends the tier's *name* and the route resolves name plus mode to a price. A link carrying `?tier=16` is still read as dollars, so nothing shared before this lands anywhere new. `fee_calculator::Scale` goes too. Four percentage widths positioning a two-segment bar with a marker and a crossover tick were saying "where your volume sits against the crossover", which is a `Meter` of sales against the crossover, overflowing when you are past it. First screen on `base.html` to own its whole document, so it owes the tail: `crate::shell::body_last` and `skip_link` are the one spelling of it now and base.html emits them too, through quasi 0.79.0's `Shell::with_body_last` and `with_body_first`. The body class is read off `Screen::measure` rather than from a route table, which answers `2790e5c4` for a converted screen. Three parity differences, each deliberate and filed: the footer's two `data-action` links are not described (`e0c0d991`), the price dials say `USD` rather than a leading `$` (quasicoherent `170f73b5`), and a described route answers 404 rather than 405 for a verb it does not hold, which moved the method-mismatch assertion to `/creators` (quasicoherent `370c0849`). Also: `embeds::document` refuses quasi 0.79.0's new outline script. Every other default script is refused by name there and the new one was not, so the two "an embed carries no script" tests went red on the pin bump.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_0136sbU8F6i9WrcvA3wn4Lgk
Author: Max Johnson <me@maxj.phd> · 2026-08-29 17:12 UTC
Signed with PGP, not checked
Commit: 1185d6045263eecbcc1893e8a0c3c00304b3c97a
Parent: 58eca21
19 files changed, +1084 insertions, -905 deletions
@@ -252,7 +252,8 @@
252 252 headline,
253 253 verdict,
254 254 crossover_note,
255 - scale: Scale::build(i.sales_per_month, crossover, verdict),
255 + sales_per_month: i.sales_per_month,
256 + crossover_sales: crossover,
256 257 }
257 258 }
258 259 }
@@ -270,80 +271,6 @@
270 271 OtherAhead,
271 272 }
272 273
273 - impl Verdict {
274 - /// CSS modifier for the headline, so the template branches on nothing.
275 - pub fn css_class(self) -> &'static str {
276 - match self {
277 - Verdict::MnwAhead => "verdict--ahead",
278 - Verdict::Even => "verdict--even",
279 - Verdict::OtherAheadForNow | Verdict::OtherAhead => "verdict--behind",
280 - }
281 - }
282 - }
283 -
284 - /// The sales axis under the numbers, marking where each platform wins.
285 - ///
286 - /// Rendered as one bar: a leading segment where the other platform costs less,
287 - /// the rest where MNW does, and a marker at the entered volume. All three are
288 - /// percentages of the bar so the template needs no units and no math.
289 - #[derive(Debug, Clone)]
290 - pub struct Scale {
291 - /// Width of the segment where the other platform wins, as a percentage.
292 - pub other_pct_width: String,
293 - /// Width of the segment where MNW wins, as a percentage.
294 - pub mnw_pct_width: String,
295 - /// Position of the "you are here" marker, as a percentage.
296 - pub marker_pct: String,
297 - /// Right-hand end of the axis, e.g. `160 sales`.
298 - pub max_label: String,
299 - /// Whether there is a crossover tick to draw at all. Kept alongside
300 - /// `crossover_label` so the template can branch without unwrapping.
301 - pub has_crossover: bool,
302 - /// Label under the crossover tick, `None` when there is no crossover.
303 - pub crossover_label: Option<String>,
304 - /// Position of the crossover tick, as a percentage. Empty with no tick.
305 - pub crossover_pct: String,
306 - }
307 -
308 - impl Scale {
309 - fn build(sales: f64, crossover: Option<f64>, verdict: Verdict) -> Self {
310 - // Show at least twice the interesting point so both regions are
311 - // visible, and never a degenerate zero-width axis.
312 - let interest = crossover.unwrap_or(0.0).max(sales);
313 - let max = (interest * 2.0).max(10.0).ceil().min(MAX_SALES * 2.0);
314 - let pct = |v: f64| format!("{:.4}", (v / max * 100.0).clamp(0.0, 100.0));
315 -
316 - match crossover {
317 - Some(c) => {
318 - let split = (c / max * 100.0).clamp(0.0, 100.0);
319 - Self {
320 - other_pct_width: format!("{split:.4}"),
321 - mnw_pct_width: format!("{:.4}", 100.0 - split),
322 - marker_pct: pct(sales),
323 - max_label: format!("{} sales", fmt_count(max)),
324 - has_crossover: true,
325 - crossover_label: Some(format!("{} sales", fmt_count(c.ceil()))),
326 - crossover_pct: format!("{split:.4}"),
327 - }
328 - }
329 - // No crossover: one region spans the whole axis. Which one depends
330 - // on whether MNW is ahead everywhere or behind everywhere.
331 - None => {
332 - let mnw_everywhere = verdict == Verdict::MnwAhead || verdict == Verdict::Even;
333 - Self {
334 - other_pct_width: if mnw_everywhere { "0" } else { "100" }.to_string(),
335 - mnw_pct_width: if mnw_everywhere { "100" } else { "0" }.to_string(),
336 - marker_pct: pct(sales),
337 - max_label: format!("{} sales", fmt_count(max)),
338 - has_crossover: false,
339 - crossover_label: None,
340 - crossover_pct: String::new(),
341 - }
342 - }
343 - }
344 - }
345 - }
346 -
347 274 /// Rendered calculator output, handed to the page and the HTMX partial.
348 275 #[derive(Debug, Clone)]
349 276 pub struct Outcome {
@@ -362,7 +289,15 @@
362 289 pub verdict: Verdict,
363 290 /// Where the crossover sits, or why there isn't one.
364 291 pub crossover_note: Option<String>,
365 - pub scale: Scale,
292 + /// The volume the dials were set to, so the panel can say where that sits
293 + /// against the crossover. Carried rather than recomputed: the reader typed
294 + /// it, `sanitize` rounded it, and a second rounding downstream is a second
295 + /// answer.
296 + pub sales_per_month: f64,
297 + /// Sales a month at which we become the cheaper place, exactly. `None` when
298 + /// our per-sale advantage is zero or negative, which is the case no volume
299 + /// closes.
300 + pub crossover_sales: Option<f64>,
366 301 }
367 302
368 303 /// Format dollars as `$1,234.56`, with the sign ahead of the `$`.
@@ -505,10 +440,8 @@
505 440 assert_eq!(out.verdict, Verdict::OtherAhead);
506 441 let note = out.crossover_note.expect("no-crossover note");
507 442 assert!(note.contains("no amount of volume"), "note was: {note}");
508 - // The whole axis belongs to the other platform.
509 - assert_eq!(out.scale.mnw_pct_width, "0");
510 - assert_eq!(out.scale.other_pct_width, "100");
511 - assert!(out.scale.crossover_label.is_none());
443 + // No volume closes the gap, so there is no crossover to point at.
444 + assert!(out.crossover_sales.is_none());
512 445 }
513 446
514 447 /// A free tier has nothing to repay, so MNW leads from the first sale and
@@ -524,7 +457,7 @@
524 457 });
525 458 assert_eq!(out.verdict, Verdict::MnwAhead);
526 459 assert!(out.crossover_note.is_none());
527 - assert_eq!(out.scale.mnw_pct_width, "100");
460 + assert!(out.crossover_sales.is_none());
528 461 }
529 462
530 463 /// Zero sales is a real answer, not a placeholder: the subscription still
@@ -605,10 +538,16 @@
605 538 approx(inf.other_pct, c.defaults.other_pct, "NaN cut");
606 539 }
607 540
608 - /// The bar's two segments always tile the axis exactly, and the marker
609 - /// stays inside it, at every dial position the inputs allow.
541 + /// A crossover is a count of sales, so it is positive and finite wherever
542 + /// there is one, at every dial position the inputs allow.
543 + ///
544 + /// This replaces `scale_segments_tile_the_axis`, which checked that two
545 + /// hand-computed bar widths summed to 100. The widths went with the
546 + /// template that positioned them; the property worth holding is the one the
547 + /// panel now states, since a meter drawn against a zero or negative total
548 + /// says nothing a reader can act on.
610 549 #[test]
611 - fn scale_segments_tile_the_axis() {
550 + fn a_crossover_is_a_positive_count_of_sales_wherever_there_is_one() {
612 551 let c = loaded();
613 552 for sales in [0.0, 1.0, 7.0, 40.0, 5_000.0, MAX_SALES] {
614 553 for price in [0.0, 1.0, 25.0, MAX_ITEM_PRICE] {
@@ -620,14 +559,17 @@
620 559 other_pct: pct,
621 560 other_per_sale: 0.30,
622 561 });
623 - let a: f64 = out.scale.other_pct_width.parse().unwrap();
624 - let b: f64 = out.scale.mnw_pct_width.parse().unwrap();
625 - assert!(
626 - (a + b - 100.0).abs() < 0.01,
627 - "segments {a} + {b} at price {price}, {sales} sales, pct {pct}"
562 + if let Some(crossover) = out.crossover_sales {
563 + assert!(
564 + crossover.is_finite() && crossover > 0.0,
565 + "crossover {crossover} at price {price}, {sales} sales, pct {pct}"
566 + );
567 + }
568 + approx(
569 + out.sales_per_month,
570 + sales,
571 + "the panel is told a volume the reader did not set",
628 572 );
629 - let m: f64 = out.scale.marker_pct.parse().unwrap();
630 - assert!((0.0..=100.0).contains(&m), "marker {m} out of the axis");
631 573 }
632 574 }
633 575 }
@@ -594,6 +594,13 @@
594 594 .fold(csrf_routes, |routes, (path, described)| {
595 595 routes.nest_service(path, described)
596 596 });
597 + // The public half of the description layer. Same envelope, different
598 + // factory: these screens resolve no session. See `quasi::public_mounts`.
599 + let csrf_routes = quasi::public_mounts(state)
600 + .into_iter()
601 + .fold(csrf_routes, |routes, (path, described)| {
602 + routes.nest_service(path, described)
603 + });
597 604 let csrf_routes = csrf_routes.finalize();
598 605 let app = Router::new()
599 606 .merge(page_routes(state.config.rate_limits))
@@ -44,8 +44,18 @@
44 44
45 45 fn parts() -> &'static quasi_webview::Parts {
46 46 static PARTS: OnceLock<quasi_webview::Parts> = OnceLock::new();
47 - PARTS.get_or_init(|| {
48 - Shell::under("/static")
47 + PARTS.get_or_init(|| described().parts())
48 + }
49 +
50 + /// The shell every document on this site is drawn in, described or templated.
51 + ///
52 + /// `base.html` takes it as [`parts`] and assembles the rest itself; a screen
53 + /// that owns its whole document (`crate::quasi::pricing`) takes the `Shell` and
54 + /// hands it to a renderer. One builder either way, which is the whole point:
55 + /// two heads written by two hands drift in ways nothing catches.
56 + #[must_use]
57 + pub fn described() -> Shell {
58 + Shell::under("/static")
49 59 // Earlier in the list = lower priority, and `makeover` is prepended
50 60 // by the renderer. A layer's position is fixed where its name is
51 61 // FIRST seen, so without the statement the generated sheets would
@@ -116,8 +126,40 @@
116 126 <script src=\"/static/upload.js?v={V}\"></script>\
117 127 <script type=\"module\" src=\"/static/dist-{V}/core/index.js\"></script>"
118 128 ))
119 - .parts()
120 - })
129 + }
130 +
131 + /// The link that jumps a keyboard reader past everything to the content.
132 + ///
133 + /// Takes the id it points at, because a described screen's first region
134 + /// publishes its own slot id and `base.html`'s `<main>` is called
135 + /// `main-content`. One spelling of the markup either way: this is an a11y
136 + /// affordance the site owes on every page, and a second copy of it is a copy
137 + /// that goes stale.
138 + #[must_use]
139 + pub fn skip_link(target: &str) -> String {
140 + format!("<a href=\"#{target}\" class=\"skip-to-main\">Skip to main content</a>")
141 + }
142 +
143 + /// What every page on this site ends with: the toast container and the classic
144 + /// script shims the `data-action` dispatcher resolves through.
145 + ///
146 + /// Called by `base.html` for the templated pages and handed to
147 + /// [`quasi_webview::Shell::with_body_last`] by a screen that owns its own
148 + /// document, so the tail is written once. Markup no description will ever name
149 + /// -- a script tag, and a container another script writes into.
150 + #[must_use]
151 + pub fn body_last() -> &'static str {
152 + concat!(
153 + "<div id=\"notifications\" class=\"toast-container\" role=\"alert\" aria-live=\"polite\"></div>",
154 + "<script src=\"/static/actions-pages.js?v=0701\"></script>",
155 + "<script src=\"/static/actions-partials.js?v=0701\"></script>",
156 + "<script src=\"/static/actions-tabs.js?v=0701\"></script>",
157 + "<script src=\"/static/actions-dashboards.js?v=0701\"></script>",
158 + "<script src=\"/static/synckit-billing.js?v=0620\"></script>",
159 + "<script src=\"/static/synckit-tabs.js?v=0623\"></script>",
160 + "<script src=\"/static/whats_new.js?v=0522\"></script>",
161 + "<script src=\"/static/markdown-editor.js?v=0820\"></script>",
162 + )
121 163 }
122 164
123 165 /// `<!doctype>` through the head's contents, without `</head>`.
@@ -6462,16 +6462,12 @@
6462 6462 PRICING CALCULATOR
6463 6463 =========================================== */
6464 6464
6465 - .pricing-page {
6466 - max-width: 800px;
6467 - }
6468 -
6469 - .pricing-input-wrap {
6470 - display: flex;
6471 - align-items: baseline;
6472 - justify-content: center;
6473 - gap: var(--gap-bound);
6474 - }
6465 + /* The calculator itself is a described screen since `1e35bc8a`
6466 + (`crate::quasi::pricing`), so it is drawn in makeover's own classes and every
6467 + rule this section held for it went with `pages/pricing.html`: the dials, the
6468 + tier cards, the list/founder toggle, the verdict, the results table and the
6469 + hand-drawn crossover bar. What is left is the one rule another template still
6470 + matches. */
6475 6471
6476 6472 .pricing-currency {
6477 6473 font-family: var(--font-display);
@@ -6479,272 +6475,6 @@
6479 6475 opacity: 0.5;
6480 6476 }
6481 6477
6482 - .pricing-input {
6483 - font-family: var(--font-display);
6484 - font-size: var(--text-display);
6485 - width: 5ch;
6486 - text-align: center;
6487 - background: transparent;
6488 - border: none;
6489 - border-bottom: 2px solid var(--border);
6490 - color: var(--content);
6491 - padding: var(--gap-bound) 0;
6492 - -moz-appearance: textfield;
6493 - }
6494 -
6495 - .pricing-input::-webkit-inner-spin-button,
6496 - .pricing-input::-webkit-outer-spin-button {
6497 - -webkit-appearance: none;
6498 - margin: 0;
6499 - }
6500 -
6501 - .pricing-input:focus {
6502 - outline: none;
6503 - border-bottom-color: var(--action);
6504 - }
6505 -
6506 - .pricing-period {
6507 - font-family: var(--font-mono);
6508 - font-size: var(--text-body);
6509 - opacity: 0.5;
6510 - }
6511 -
6512 - /* <mnw-price-mode> is a custom element (default display:inline); it wraps the
6513 - toggle and the tier grid, so it has to lay out as a block. */
6514 - mnw-price-mode {
6515 - display: block;
6516 - }
6517 -
6518 - /* List-vs-founder toggle above the tier cards, rendered only while the founder
6519 - window is open. Segmented control rather than a checkbox: both options are
6520 - real prices the visitor can be shown, and neither is an "off" state. Follows
6521 - .tier-option's pattern of hiding the radio and styling the label off
6522 - :has(:checked), so the selection needs no JS class. */
6523 - .price-mode {
6524 - display: inline-flex;
6525 - gap: 0;
6526 - margin-bottom: var(--gap-section);
6527 - border: 1px solid var(--border);
6528 - border-radius: var(--radius-control);
6529 - overflow: hidden;
6530 - }
6531 -
6532 - .price-mode-option {
6533 - cursor: pointer;
6534 - font-family: var(--font-mono);
6535 - font-size: var(--text-note);
6536 - padding: var(--gap-bound) var(--gap-section);
6537 - transition: background-color 0.15s ease;
6538 - }
6539 -
6540 - .price-mode-option input[type="radio"] {
6541 - position: absolute;
6542 - opacity: 0;
6543 - pointer-events: none;
6544 - }
6545 -
6546 - .price-mode-option:has(input[type="radio"]:checked) {
6547 - background: var(--action);
6548 - color: var(--content-on-action);
6549 - }
6550 -
6551 - .price-mode-option:focus-within {
6552 - outline: 2px solid var(--focus-ring);
6553 - outline-offset: -2px;
6554 - }
6555 -
6556 - .tier-selector {
6557 - display: grid;
6558 - grid-template-columns: 1fr 1fr;
6559 - gap: var(--gap-section);
6560 - text-align: left;
6561 - }
6562 -
6563 - .tier-option {
6564 - cursor: pointer;
6565 - transition: border-color 0.15s ease;
6566 - }
6567 -
6568 - .tier-option input[type="radio"] {
6569 - position: absolute;
6570 - opacity: 0;
6571 - pointer-events: none;
6572 - }
6573 -
6574 - .tier-option:focus-within {
6575 - outline: 2px solid var(--focus-ring);
6576 - outline-offset: 2px;
6577 - }
6578 -
6579 - /* Selected tier, driven purely by the checked radio (no JS toggling a
6580 - class). The `.is-selected` alias is retained for any server-rendered use. */
6581 - .tier-option.is-selected,
6582 - .tier-option:has(input[type="radio"]:checked) {
6583 - border-color: var(--action);
6584 - border-width: 2px;
6585 - padding: calc(var(--gap-section) - 1px);
6586 - }
6587 -
6588 - /* Two dials side by side (price + volume, then the other platform's two).
6589 - Stacks on narrow screens via the mobile block below. */
6590 - .calc-dials {
6591 - display: grid;
6592 - grid-template-columns: 1fr 1fr;
6593 - gap: var(--gap-section);
6594 - align-items: end;
6595 - }
6596 -
6597 - .calc-dial {
6598 - display: flex;
6599 - flex-direction: column;
6600 - align-items: center;
6601 - gap: var(--gap-bound);
6602 - }
6603 -
6604 - .calc-dial-label {
6605 - font-family: var(--font-mono);
6606 - font-size: var(--text-note);
6607 - opacity: 0.7;
6608 - }
6609 -
6610 - /* Who comes out ahead at the current dials. The three states are the only
6611 - place the calculator uses color to make a claim, so they carry the same
6612 - weight in both directions: losing is stated as plainly as winning. */
6613 - .calc-verdict {
6614 - font-family: var(--font-display);
6615 - font-size: var(--text-title);
6616 - text-align: center;
6617 - padding: var(--gap-pane);
6618 - background: var(--surface-overlay);
6619 - border: 1px solid var(--border);
6620 - border-left-width: 3px;
6621 - }
6622 -
6623 - .verdict--ahead {
6624 - border-left-color: var(--success);
6625 - }
6626 -
6627 - .verdict--even {
6628 - border-left-color: var(--border);
6629 - }
6630 -
6631 - .verdict--behind {
6632 - border-left-color: var(--warning);
6633 - }
6634 -
6635 - .calc-table th[scope="row"] {
6636 - text-align: left;
6637 - font-weight: 400;
6638 - opacity: 0.7;
6639 - }
6640 -
6641 - .calc-table .calc-gross,
6642 - .calc-keep-row td {
6643 - font-family: var(--font-mono);
6644 - }
6645 -
6646 - .calc-keep-row td {
6647 - font-size: var(--text-title);
6648 - }
6649 -
6650 - /* The win-region bar. Two segments tile the full width; the server sends both
6651 - widths as percentages, so nothing here is computed in the browser. */
6652 - .calc-scale {
6653 - display: flex;
6654 - flex-direction: column;
6655 - gap: var(--gap-bound);
6656 - }
6657 -
6658 - .calc-bar {
6659 - position: relative;
6660 - display: flex;
6661 - height: 1.5rem;
6662 - border: 1px solid var(--border);
6663 - overflow: hidden;
6664 - }
6665 -
6666 - .calc-bar-seg {
6667 - height: 100%;
6668 - }
6669 -
6670 - .calc-bar-seg--other {
6671 - background: var(--warning);
6672 - opacity: 0.35;
6673 - }
6674 -
6675 - .calc-bar-seg--mnw {
6676 - background: var(--success);
6677 - opacity: 0.35;
6678 - }
6679 -
6680 - /* "You are here" on the sales axis. */
6681 - .calc-marker {
6682 - position: absolute;
6683 - top: 0;
6684 - bottom: 0;
6685 - width: 2px;
6686 - margin-left: -1px;
6687 - background: var(--content);
6688 - }
6689 -
6690 - .calc-crossover {
6691 - position: absolute;
6692 - top: 0;
6693 - bottom: 0;
6694 - width: 1px;
6695 - background: var(--content);
6696 - opacity: 0.4;
6697 - }
6698 -
6699 - .calc-axis {
6700 - display: flex;
6701 - justify-content: space-between;
6702 - gap: var(--gap-bound);
6703 - font-family: var(--font-mono);
6704 - font-size: var(--text-fine);
6705 - opacity: 0.6;
6706 - }
6707 -
6708 - .calc-axis-crossover {
6709 - opacity: 0.9;
6710 - }
6711 -
6712 - .calc-legend {
6713 - display: flex;
6714 - flex-wrap: wrap;
6715 - gap: var(--gap-section);
6716 - font-size: var(--text-fine);
6717 - opacity: 0.7;
6718 - }
6719 -
6720 - .calc-legend-item::before {
6721 - content: "";
6722 - display: inline-block;
6723 - width: 0.75rem;
6724 - height: 0.75rem;
6725 - margin-right: var(--gap-bound);
6726 - vertical-align: baseline;
6727 - }
6728 -
6729 - .calc-legend-item--other::before {
6730 - background: var(--warning);
6731 - opacity: 0.35;
6732 - }
6733 -
6734 - .calc-legend-item--mnw::before {
6735 - background: var(--success);
6736 - opacity: 0.35;
6737 - }
6738 -
6739 - .calc-note {
6740 - font-family: var(--font-mono);
6741 - font-size: var(--text-note);
6742 - padding: var(--gap-section);
6743 - background: var(--surface-overlay);
6744 - border-left: 3px solid var(--border);
6745 - line-height: 1.6;
6746 - }
6747 -
6748 6478 /* ===========================================
6749 6479 DOCUMENTATION PAGES
6750 6480 =========================================== */
@@ -29,7 +29,7 @@
29 29 {% block head %}{% endblock %}
30 30 </head>
31 31 <body{{ crate::shell::body_attrs()|safe }}{% block body_attrs %}{% endblock %}>
32 - <a href="#main-content" class="skip-to-main">Skip to main content</a>
32 + {{ crate::shell::skip_link("main-content")|safe }}
33 33 <main id="main-content">
34 34 {% block content %}{% endblock %}
35 35 </main>
@@ -52,26 +52,11 @@
52 52 <p>&copy; 2026 Make Creative, LLC</p>
53 53 </footer>
54 54
55 - <!-- Toast notification container -->
56 - <div id="notifications" class="toast-container" role="alert" aria-live="polite"></div>
57 -
58 - <!-- Delegated on* handlers, extracted from inline attributes so the CSP can
59 - drop script-src 'unsafe-inline'. The data-action dispatcher + core
60 - primitives now live in the typed core module (frontend/src/core),
61 - loaded as an ES module in <head> via crate::shell; these classic
62 - shims resolve through its registry / window bridge. -->
63 - <script src="/static/actions-pages.js?v=0701"></script>
64 - <script src="/static/actions-partials.js?v=0701"></script>
65 - <script src="/static/actions-tabs.js?v=0701"></script>
66 - <script src="/static/actions-dashboards.js?v=0701"></script>
67 - <script src="/static/synckit-billing.js?v=0620"></script>
68 - <script src="/static/synckit-tabs.js?v=0623"></script>
69 - <script src="/static/whats_new.js?v=0522"></script>
70 - {# Binds the described markdown field's Write/Preview chrome wherever one
71 - appears. Loaded here rather than beside each of the four surfaces because
72 - three of them arrive inside HTMX-swapped partials, and the binder rescans
73 - on settle. #}
74 - <script src="/static/markdown-editor.js?v=0820"></script>
55 + {# The toast container and the classic `data-action` shims, emitted from
56 + `crate::shell` rather than written here: a screen that owns its whole
57 + document (`crate::quasi::pricing`) owes the same tail, and two copies of
58 + it drift. #}
59 + {{ crate::shell::body_last()|safe }}
75 60 {% block scripts %}{% endblock %}
76 61 </body>
77 62 </html>