@kitlangton/knobs 0.4.0-next.3 → 0.4.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ui-react.js CHANGED
@@ -608,7 +608,12 @@ var compareCss = `
608
608
  /* Glass: what's behind shows through, blurred, on the knobs tokens. */
609
609
  .knobs-cmp-label,.knobs-cmp-zoom{background:color-mix(in srgb,var(--_k-bg) 52%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.6);backdrop-filter:blur(14px) saturate(1.6);
610
610
  box-shadow:inset 0 0 0 .5px var(--_k-border),inset 0 1px 0 var(--_k-rim),0 2px 10px rgba(0,0,0,calc(.18 * var(--_k-shadow-k,1)))}
611
- .knobs-cmp-label{position:absolute;left:0;top:0;padding:1px 7px;border-radius:999px;font-size:var(--_k-text-meta);line-height:16px;color:var(--_k-fg-2);white-space:nowrap;pointer-events:none;will-change:transform;transition:color calc(var(--_k-hover-out) * 1ms)}
611
+ /* ✗ Before · ✓ After: Before quieter (fg-3), After in full text. Neutral: no colour of its own. */
612
+ .knobs-cmp-label{position:absolute;left:0;top:0;display:flex;align-items:center;gap:4px;padding:1px 7px 1px 5px;border-radius:999px;font-size:var(--_k-text-meta);line-height:16px;font-weight:var(--_k-weight-label);
613
+ white-space:nowrap;pointer-events:none;will-change:transform}
614
+ .knobs-cmp-label[data-side="before"]{color:var(--_k-fg-3)}
615
+ .knobs-cmp-label[data-side="after"]{color:var(--_k-fg)}
616
+ .knobs-cmp-mark svg{display:block;width:12px;height:12px}
612
617
  /* The wipe: a hairline down the line with a raised grip at the pointer's height; a wider strip
613
618
  takes the hand and the keys (a slider). It fades with the mode; wiping, line and grip brighten. */
614
619
  .knobs-cmp-handle{cursor:ew-resize;position:absolute;left:-12px;top:0;width:24px;height:0;opacity:0;pointer-events:none;outline:none;will-change:transform;transition:opacity .18s cubic-bezier(.4,0,1,1)}
@@ -1332,6 +1337,22 @@ var init_schema = __esm(() => {
1332
1337
  source: css("var(--k-warn)"),
1333
1338
  doc: "Modified lines."
1334
1339
  },
1340
+ {
1341
+ name: "before",
1342
+ tier: "semantic",
1343
+ type: "color",
1344
+ group: "Data",
1345
+ source: css("var(--k-fg-3)"),
1346
+ doc: "Before in a comparison, and only Before: its label and side of the seam. Neutral unless a theme gives it a colour."
1347
+ },
1348
+ {
1349
+ name: "after",
1350
+ tier: "semantic",
1351
+ type: "color",
1352
+ group: "Data",
1353
+ source: css("var(--k-fg)"),
1354
+ doc: "After in a comparison, and only After."
1355
+ },
1335
1356
  {
1336
1357
  name: "series-1",
1337
1358
  tier: "semantic",
@@ -1422,6 +1443,150 @@ var init_schema = __esm(() => {
1422
1443
  source: value("288px"),
1423
1444
  doc: "Panel width."
1424
1445
  },
1446
+ {
1447
+ name: "space-inside",
1448
+ tier: "semantic",
1449
+ type: "length",
1450
+ group: "Space",
1451
+ source: css("var(--k-unit)"),
1452
+ doc: "Parts of one thing: an icon and its label, a meta line and its title."
1453
+ },
1454
+ {
1455
+ name: "space-sibling",
1456
+ tier: "semantic",
1457
+ type: "length",
1458
+ group: "Space",
1459
+ source: css("calc(var(--k-unit) * 2)"),
1460
+ doc: "Siblings in a group: buttons in a row, a list item's lines."
1461
+ },
1462
+ {
1463
+ name: "space-title",
1464
+ tier: "semantic",
1465
+ type: "length",
1466
+ group: "Space",
1467
+ source: css("calc(var(--k-unit) * 3)"),
1468
+ doc: "A heading and what it heads."
1469
+ },
1470
+ {
1471
+ name: "space-group",
1472
+ tier: "semantic",
1473
+ type: "length",
1474
+ group: "Space",
1475
+ source: css("calc(var(--k-unit) * 12)"),
1476
+ doc: "Groups inside a section (prose, video, diff), stacked media, feed items (a hairline in the middle)."
1477
+ },
1478
+ {
1479
+ name: "space-section",
1480
+ tier: "semantic",
1481
+ type: "length",
1482
+ group: "Space",
1483
+ source: css("calc(var(--k-unit) * 24)"),
1484
+ doc: "Sections of a page (the intro, the updates, each PR), a hairline in the middle."
1485
+ },
1486
+ {
1487
+ name: "space-column",
1488
+ tier: "semantic",
1489
+ type: "length",
1490
+ group: "Space",
1491
+ source: css("calc(var(--k-unit) * 6)"),
1492
+ doc: "Columns: a contents rail and the page, media side by side."
1493
+ },
1494
+ {
1495
+ name: "pad-page",
1496
+ tier: "semantic",
1497
+ type: "length",
1498
+ group: "Space",
1499
+ source: css("calc(var(--k-unit) * 6)"),
1500
+ doc: "A page's edge; 16px under 640px wide."
1501
+ },
1502
+ {
1503
+ name: "pad-card",
1504
+ tier: "semantic",
1505
+ type: "length",
1506
+ group: "Space",
1507
+ source: css("calc(var(--k-unit) * 3)"),
1508
+ doc: "Inside a card or popover."
1509
+ },
1510
+ {
1511
+ name: "type-heading",
1512
+ tier: "semantic",
1513
+ type: "length",
1514
+ group: "Type",
1515
+ source: value("20px"),
1516
+ doc: "Section headings (a PR's title, under its kicker), weight 500."
1517
+ },
1518
+ {
1519
+ name: "leading-heading",
1520
+ tier: "semantic",
1521
+ type: "length",
1522
+ group: "Type",
1523
+ source: value("28px"),
1524
+ doc: "Line height for type-heading."
1525
+ },
1526
+ {
1527
+ name: "type-title",
1528
+ tier: "semantic",
1529
+ type: "length",
1530
+ group: "Type",
1531
+ source: value("16px"),
1532
+ doc: "A page's title, weight 500."
1533
+ },
1534
+ {
1535
+ name: "leading-title",
1536
+ tier: "semantic",
1537
+ type: "length",
1538
+ group: "Type",
1539
+ source: value("24px"),
1540
+ doc: "Line height for type-title."
1541
+ },
1542
+ {
1543
+ name: "type-body",
1544
+ tier: "semantic",
1545
+ type: "length",
1546
+ group: "Type",
1547
+ source: value("13px"),
1548
+ doc: "Prose, questions, list rows, step titles; 400, or 500 for emphasis."
1549
+ },
1550
+ {
1551
+ name: "leading-body",
1552
+ tier: "semantic",
1553
+ type: "length",
1554
+ group: "Type",
1555
+ source: value("20px"),
1556
+ doc: "Line height for type-body."
1557
+ },
1558
+ {
1559
+ name: "type-label",
1560
+ tier: "semantic",
1561
+ type: "length",
1562
+ group: "Type",
1563
+ source: value("12px"),
1564
+ doc: "Labels over media, weight 500; mono inside body text."
1565
+ },
1566
+ {
1567
+ name: "leading-label",
1568
+ tier: "semantic",
1569
+ type: "length",
1570
+ group: "Type",
1571
+ source: value("16px"),
1572
+ doc: "Line height for type-label."
1573
+ },
1574
+ {
1575
+ name: "type-small",
1576
+ tier: "semantic",
1577
+ type: "length",
1578
+ group: "Type",
1579
+ source: value("11px"),
1580
+ doc: "Meta, controls, keys, timestamps; mono inside labels."
1581
+ },
1582
+ {
1583
+ name: "leading-small",
1584
+ tier: "semantic",
1585
+ type: "length",
1586
+ group: "Type",
1587
+ source: value("16px"),
1588
+ doc: "Line height for type-small."
1589
+ },
1425
1590
  {
1426
1591
  name: "font-size",
1427
1592
  tier: "semantic",
@@ -2386,16 +2551,18 @@ function injectTokens(doc) {
2386
2551
  style.textContent = rootCss;
2387
2552
  doc.head.append(style);
2388
2553
  }
2389
- var ROOT_CLASS = "knobs-root", extras, spring, rootCss;
2554
+ var ROOT_CLASS = "knobs-root", extras, narrow, spring, rootCss;
2390
2555
  var init_root = __esm(() => {
2391
2556
  init_knobs();
2392
2557
  init_css();
2393
2558
  init_glide();
2394
2559
  init_motion();
2395
2560
  extras = `:where(.${ROOT_CLASS}){--_k-shadow-k:calc(var(--k-shadow-strength,calc(.48 - .32 * var(--_k-light,0))) / .48);--_k-glass-tone:var(--k-glass-tone,calc(.5 + .58 * var(--_k-light,0)));--_k-fill-base:var(--k-fill-base,var(--_k-fill-selected));--_k-fill-hover-mix:var(--k-fill-hover-mix,var(--k-fill-rest-mix,20%));--_k-reveal-ease:var(--k-ease,cubic-bezier(.2,.8,.2,1))}`;
2561
+ narrow = `@media(max-width:639.98px){:where(.${ROOT_CLASS}){--_k-pad-page:var(--k-pad-page,calc(var(--_k-unit) * 4))}}`;
2396
2562
  spring = `:where(:root,:host){--k-fill-ease:${DEFAULT_LINEAR.easing};--k-fill-ms:${DEFAULT_LINEAR.ms}}`;
2397
2563
  rootCss = `${tokensCss(knobs)}
2398
2564
  ${extras}
2565
+ ${narrow}
2399
2566
  ${spring}
2400
2567
  ${DRIVERS}
2401
2568
  ${motionCss}`;
@@ -2606,6 +2773,12 @@ var css2 = `
2606
2773
  --_ku-pane-bg and --_ku-pane-ring so this reaches them. */
2607
2774
  :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-ui:not(.knobs-surface),.knobs-ui[data-nested]{--_ku-gap:var(--k-ui-gap,0px);--_ku-pane-bg:transparent;--_ku-pane-ring:none;
2608
2775
  --_ku-divider:var(--_k-split-gutter);--_kd-bg:transparent}
2776
+ /* What draws its box without those: a picker's card keeps its fill as the highlight but drops its
2777
+ ring; a timeline's track drops its well for a hairline under the lane; an inline panel sheds its
2778
+ card (--k-chrome 0). A full-window step is the one raised exception (it is the surface). */
2779
+ :is(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-picker-card,.knobs-ui[data-nested] .knobs-picker-card{box-shadow:none}
2780
+ :is(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-tl-track,.knobs-ui[data-nested] .knobs-tl-track{background:transparent;border-radius:0;box-shadow:inset 0 -1px 0 var(--_k-border-subtle)}
2781
+ :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-panel.knobs-inline:not(.knobs-page){--k-chrome:0}
2609
2782
  /* Only the pieces' own elements take their type and resets: what you put in a pane is yours. */
2610
2783
  .knobs-ui{box-sizing:border-box}
2611
2784
  :where(.knobs-split>*,.knobs-pane-clip,.knobs-pane-inner,.knobs-pane-body,.knobs-pane-head,.knobs-pane-head *,.knobs-pane-rail,.knobs-pane-rail *,.knobs-tree,.knobs-tree *,.knobs-tree-ghost){box-sizing:border-box}
@@ -10954,6 +11127,17 @@ function compare(container, options) {
10954
11127
  const labels = [el("div", "knobs-cmp-label"), el("div", "knobs-cmp-label")];
10955
11128
  labels[0].dataset.side = "before";
10956
11129
  labels[1].dataset.side = "after";
11130
+ const setLabel = (index, text) => {
11131
+ const l = labels[index];
11132
+ if (!l.firstElementChild) {
11133
+ const mark = el("span", "knobs-cmp-mark");
11134
+ mark.innerHTML = icon(index === 0 ? "x" : "check", { small: true });
11135
+ l.append(mark, el("span", "knobs-cmp-name"));
11136
+ }
11137
+ const name = l.lastElementChild;
11138
+ if (name.textContent !== text)
11139
+ name.textContent = text;
11140
+ };
10957
11141
  const handle = el("div", "knobs-cmp-handle");
10958
11142
  handle.setAttribute("role", "slider");
10959
11143
  handle.setAttribute("aria-label", "Wipe");
@@ -12015,7 +12199,7 @@ function compare(container, options) {
12015
12199
  old.preview.replaceWith(s.preview);
12016
12200
  sides[index] = s;
12017
12201
  note.textContent = "";
12018
- labels[index].textContent = labelOf(index);
12202
+ setLabel(index, labelOf(index));
12019
12203
  if (near)
12020
12204
  loadSide(s);
12021
12205
  readColors();
@@ -12026,8 +12210,8 @@ function compare(container, options) {
12026
12210
  stage.insertBefore(sides[1].preview, labels[0]);
12027
12211
  stage.insertBefore(sides[0].el, labels[0]);
12028
12212
  stage.insertBefore(sides[0].preview, labels[0]);
12029
- labels[0].textContent = labelOf(0);
12030
- labels[1].textContent = labelOf(1);
12213
+ setLabel(0, labelOf(0));
12214
+ setLabel(1, labelOf(1));
12031
12215
  loupeBtn.setAttribute("aria-pressed", String(loupeOn));
12032
12216
  loopBtn.setAttribute("aria-pressed", String(loopNow() !== null));
12033
12217
  const modePill = modeList.length > 1 ? selectionPill(modes, {
@@ -12167,11 +12351,11 @@ function compare(container, options) {
12167
12351
  if (before)
12168
12352
  replace(0, before);
12169
12353
  else if (next.before)
12170
- labels[0].textContent = next.before.label ?? "Before";
12354
+ setLabel(0, next.before.label ?? "Before");
12171
12355
  if (after)
12172
12356
  replace(1, after);
12173
12357
  else if (next.after)
12174
- labels[1].textContent = next.after.label ?? "After";
12358
+ setLabel(1, next.after.label ?? "After");
12175
12359
  if (before || after)
12176
12360
  strip?.update({ ...before ? { before } : {}, ...after ? { after } : {} });
12177
12361
  if (next.before || next.after)
@@ -16714,7 +16898,9 @@ function confirm(options) {
16714
16898
  }
16715
16899
 
16716
16900
  // src/ui/walkthrough.ts
16901
+ init_icons();
16717
16902
  init_look();
16903
+ init_selection_pill();
16718
16904
 
16719
16905
  // src/ui/toast.ts
16720
16906
  init_look();
@@ -16780,404 +16966,16 @@ function toast(message, options = {}) {
16780
16966
  };
16781
16967
  }
16782
16968
 
16783
- // src/ui/walkthrough-media.ts
16784
- init_compare_model();
16785
- init_glide();
16786
- init_look();
16787
-
16788
- // src/ui/walkthrough-css.ts
16969
+ // src/ui/walkthrough-blocks.ts
16789
16970
  init_motion();
16790
- var snappy3 = springCss(SPRINGS.snappy);
16791
- var calm = springCss(SPRINGS.calm);
16792
- var walkthroughCss = `
16793
- :where(.knobs-walkthrough,.knobs-walkthrough *,.knobs-wt-slot,.knobs-wt-slot *){box-sizing:border-box}
16794
- .knobs-ui.knobs-walkthrough{--kw-snappy:${snappy3.easing};--kw-snappy-ms:${snappy3.duration}ms;--kw-calm:${calm.easing};--kw-calm-ms:${calm.duration}ms;
16795
- --kw-text:var(--k-walkthrough-text,14px);--kw-measure:var(--k-walkthrough-measure,68ch);--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info));
16796
- --kw-card:var(--_ku-pane-bg);--kw-card-ring:var(--_ku-pane-ring);--kw-pad:16px;--kw-ink:var(--k-walkthrough-ink,#ff375f);
16797
- container:knobs-wt / inline-size;display:block;width:100%;color:var(--_k-fg);font:var(--_k-weight) var(--kw-text)/1.5 var(--_k-font);text-align:left;
16798
- letter-spacing:normal;-webkit-font-smoothing:antialiased}
16799
- .knobs-walkthrough [hidden]{display:none!important}
16800
- .knobs-walkthrough :is(h1,h2,h3,p){margin:0;font:inherit;letter-spacing:inherit}
16801
- .knobs-walkthrough a{color:inherit;text-decoration:underline;text-decoration-color:color-mix(in srgb,currentColor 35%,transparent);text-underline-offset:2px;transition:text-decoration-color .15s}
16802
- .knobs-walkthrough a:hover{text-decoration-color:currentColor}
16803
- .knobs-walkthrough code,.knobs-wt-slot code{font:calc(1em - 1px) var(--_k-mono);padding:1px 5px;border-radius:5px;background:var(--_k-fill)}
16804
- :where(.knobs-ui) :is(.knobs-wt-btn,.knobs-wt-pick,.knobs-wt-toc-step,button.knobs-wt-stitle,.knobs-wt-chapter){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;
16805
- background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
16806
-
16807
- /* ——— layout: the rail beside the column; one column where it's narrow ——— */
16808
- .knobs-wt-layout{display:grid;grid-template-columns:200px minmax(0,1fr);gap:40px;align-items:start}
16809
- .knobs-wt-main{display:flex;flex-direction:column;gap:56px;min-width:0;max-width:1120px;padding-bottom:40vh}
16810
- @container knobs-wt (max-width:880px){.knobs-wt-layout{grid-template-columns:minmax(0,1fr);gap:0}.knobs-wt-main{gap:44px}}
16811
-
16812
- /* ——— the page's head ——— */
16813
- .knobs-wt-head{display:flex;flex-direction:column;gap:10px}
16814
- .knobs-wt-title{font-size:calc(var(--kw-text) + 10px)!important;font-weight:var(--_k-weight-title)!important;line-height:1.2;letter-spacing:-.01em!important}
16815
- .knobs-wt-summary{max-width:var(--kw-measure);color:var(--_k-fg-2)}
16816
- .knobs-wt-meta{display:flex;flex-wrap:wrap;gap:6px 16px;color:var(--_k-fg-3);font-size:calc(var(--kw-text) - 2px)}
16817
- .knobs-wt-meta>span{display:inline-flex;gap:6px;white-space:nowrap}
16818
- .knobs-wt-meta b{font-weight:var(--_k-weight);color:var(--_k-fg-2);font-family:var(--_k-mono);font-size:calc(1em - .5px)}
16819
-
16820
- /* ——— a section: a PR or topic ——— */
16821
- .knobs-wt-section{display:flex;flex-direction:column;gap:14px;min-width:0}
16822
- .knobs-wt-shead{display:flex;flex-direction:column;gap:6px;scroll-margin-top:16px}
16823
- .knobs-wt-kicker{display:flex;align-items:center;gap:10px;font-size:calc(var(--kw-text) - 2px);color:var(--_k-fg-3)}
16824
- .knobs-wt-kicker a{font-family:var(--_k-mono);color:var(--_k-fg-2);text-decoration:none}
16825
- .knobs-wt-kicker a:hover{color:var(--_k-fg)}
16826
- .knobs-wt-stitle{font-size:calc(var(--kw-text) + 4px)!important;font-weight:var(--_k-weight-title)!important;line-height:1.3}
16827
- .knobs-wt-ssum{max-width:var(--kw-measure);color:var(--_k-fg-2)}
16828
- .knobs-wt-chip{display:inline-flex;align-items:center;gap:6px;height:20px;padding:0 8px;border-radius:999px;font-size:calc(var(--kw-text) - 3px);font-weight:var(--_k-weight-label);white-space:nowrap;
16829
- --kw-tone:var(--_k-fg-2);color:var(--kw-tone);background:color-mix(in srgb,var(--kw-tone) 14%,transparent)}
16830
- .knobs-wt-chip::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
16831
- :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="good"]{--kw-tone:var(--kw-good)}
16832
- :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="warn"]{--kw-tone:var(--_k-warn)}
16833
- :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="bad"]{--kw-tone:var(--_k-danger)}
16834
- :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="info"]{--kw-tone:var(--kw-info)}
16835
- :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="dim"]{--kw-tone:var(--_k-fg-2)}
16836
-
16837
- /* ——— a step: one card ——— */
16838
- .knobs-wt-step{position:relative;display:flex;flex-direction:column;gap:12px;padding:var(--kw-pad);border-radius:var(--_k-radius-panel);background:var(--kw-card);box-shadow:var(--kw-card-ring);
16839
- scroll-margin-top:16px;outline:none;container:knobs-wt-step / inline-size}
16840
- .knobs-walkthrough[data-frame="flat"] .knobs-wt-step{box-shadow:inset 0 0 0 1px var(--_k-border)}
16841
- .knobs-wt-step-head{display:flex;align-items:baseline;gap:10px;min-width:0}
16842
- .knobs-wt-num{flex:none;font:var(--_k-weight) calc(var(--kw-text) - 2px)/1 var(--_k-mono);color:var(--_k-fg-3);font-variant-numeric:tabular-nums}
16843
- .knobs-wt-stepTitle{flex:1;min-width:0;font-weight:var(--_k-weight-title)!important}
16844
- .knobs-wt-caption{max-width:var(--kw-measure);color:var(--_k-fg-2);margin-top:-6px!important}
16845
- .knobs-wt-question{font-weight:var(--_k-weight-label)!important}
16846
- /* The step Kit is on: its number lights, and its choices show their keys. */
16847
- .knobs-wt-step[data-current] .knobs-wt-num{color:var(--_k-fg)}
16848
- .knobs-wt-media{display:grid;grid-template-columns:repeat(var(--kw-n,1),minmax(0,1fr));gap:12px;min-width:0}
16849
- @container knobs-wt-step (max-width:640px){.knobs-wt-media{grid-template-columns:minmax(0,1fr)}}
16850
-
16851
- /* ——— the feed: the agent's updates and answers, newest first, compact ——— */
16852
- .knobs-wt-feed{margin-top:-24px}
16853
- .knobs-wt-feed-list{list-style:none;margin:0;padding:0;max-height:260px;overflow:auto;scrollbar-width:none;display:flex;flex-direction:column}
16854
- .knobs-wt-feed-list::-webkit-scrollbar{display:none}
16855
- .knobs-wt-fi{display:grid;grid-template-columns:64px 56px minmax(0,1fr) auto;gap:10px;align-items:baseline;padding:6px 0;font-size:calc(var(--kw-text) - 1px)}
16856
- .knobs-wt-fi+.knobs-wt-fi{box-shadow:inset 0 1px 0 var(--_k-border)}
16857
- .knobs-wt-fi-when{color:var(--_k-fg-3);font-size:calc(var(--kw-text) - 2.5px);font-variant-numeric:tabular-nums;white-space:nowrap}
16858
- .knobs-wt-fi-kind{color:var(--_k-fg-2);font-size:calc(var(--kw-text) - 2.5px);font-weight:var(--_k-weight-label)}
16859
- .knobs-wt-fi[data-kind="qa"] .knobs-wt-fi-kind{color:var(--kw-info)}
16860
- .knobs-wt-fi-body{max-width:var(--kw-measure);color:var(--_k-fg)}
16861
- .knobs-wt-fi-q{color:var(--_k-fg-2)}
16862
- .knobs-wt-fi-q::before{content:"You asked: ";color:var(--_k-fg-3)}
16863
- :where(.knobs-ui) :is(.knobs-wt-fi-step,.knobs-wt-steplink){all:revert;box-sizing:border-box;font:var(--_k-weight) calc(var(--k-walkthrough-text,14px) - 2.5px)/1 var(--_k-mono);color:var(--_k-fg-2);background:transparent;border:0;
16864
- padding:3px 6px;margin:0;border-radius:6px;cursor:pointer;outline:none;transition:background .15s,color .15s}
16865
- .knobs-ui :is(.knobs-wt-fi-step,.knobs-wt-steplink):is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
16866
-
16867
- /* ——— a step's sent notes ——— */
16868
- .knobs-wt-notes{display:flex;flex-direction:column}
16869
- .knobs-wt-notes:empty{display:none}
16870
- .knobs-wt-n{display:flex;align-items:flex-start;gap:10px;padding:6px 0;overflow:hidden;
16871
- transition:height var(--kw-snappy-ms) var(--kw-snappy),opacity var(--kw-snappy-ms) var(--kw-snappy),filter var(--kw-snappy-ms) var(--kw-snappy)}
16872
- .knobs-wt-n[data-entering]{opacity:0;filter:blur(3px)}
16873
- .knobs-wt-n[data-leaving]{opacity:0;filter:blur(3px);padding-block:0}
16874
- .knobs-wt-n+.knobs-wt-n{box-shadow:inset 0 1px 0 var(--_k-border)}
16875
- .knobs-wt-n-meta{flex:none;min-width:44px;padding-top:2px;color:var(--_k-fg-3);font:var(--_k-weight) calc(var(--kw-text) - 2.5px)/1.4 var(--_k-mono);font-variant-numeric:tabular-nums}
16876
- .knobs-wt-n-meta[data-seek]{cursor:pointer}
16877
- .knobs-wt-n-meta[data-seek]:hover{color:var(--_k-fg)}
16878
- .knobs-wt-n-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px;font-size:calc(var(--kw-text) - 1px)}
16879
- .knobs-wt-n-voice{height:30px;max-width:320px}
16880
- .knobs-wt-n-ink{display:block;width:120px;max-height:80px;border-radius:6px;background:var(--_k-fill)}
16881
- .knobs-wt-n-ink path{fill:none;stroke:var(--kw-ink);stroke-width:2px;vector-effect:non-scaling-stroke;stroke-linecap:round;stroke-linejoin:round}
16882
-
16883
- /* ——— the note bar, docked at the bottom of the column ——— */
16884
- .knobs-wt-dock{position:sticky;bottom:12px;z-index:40;margin-top:-24px}
16885
- .knobs-wt-notebar{display:flex;flex-direction:column;gap:2px;padding:6px 6px 6px 14px;border-radius:calc(var(--_k-radius-panel) + 2px);background:var(--_k-bg);
16886
- box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay)}
16887
- .knobs-wt-on{padding-top:3px;color:var(--_k-fg-3);font-size:calc(var(--kw-text) - 2.5px);white-space:nowrap;overflow:hidden;
16888
- -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent)}
16889
- .knobs-wt-row{display:flex;align-items:flex-end;gap:4px}
16890
- :where(.knobs-ui) :is(.knobs-wt-input,.knobs-wt-tool,.knobs-wt-x,.knobs-wt-back){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;
16891
- border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
16892
- .knobs-ui .knobs-wt-input{flex:1;min-width:0;height:32px;max-height:140px;padding:6px 0;resize:none;color:var(--_k-fg);caret-color:var(--_k-accent);line-height:20px;font-size:var(--kw-text)}
16893
- .knobs-ui .knobs-wt-input::placeholder{color:var(--_k-fg-3)}
16894
- .knobs-wt-attach{display:flex;gap:4px;align-items:center;align-self:center}
16895
- .knobs-wt-attach:empty{display:none}
16896
- .knobs-wt-chip-a{display:inline-flex;align-items:center;gap:2px;height:24px;padding:0 2px 0 9px;border-radius:999px;background:var(--_k-fill-selected);font-size:calc(var(--kw-text) - 2.5px);white-space:nowrap}
16897
- .knobs-ui .knobs-wt-x{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;color:var(--_k-fg-3);cursor:pointer;transition:background .15s,color .15s}
16898
- .knobs-ui .knobs-wt-x:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
16899
- .knobs-wt-x svg{width:10px;height:10px}
16900
- .knobs-ui .knobs-wt-tool{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;min-width:32px;height:32px;padding:0 8px;border-radius:calc(var(--_k-radius) + 2px);
16901
- color:var(--_k-fg-2);cursor:pointer;transition:background .15s,color .15s}
16902
- .knobs-ui .knobs-wt-tool:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
16903
- .knobs-ui .knobs-wt-tool[aria-pressed="true"]{background:var(--_k-fill-selected);color:var(--_k-fg)}
16904
- .knobs-wt-tool svg{width:15px;height:15px}
16905
- .knobs-wt-rec{font-size:calc(var(--kw-text) - 2.5px);font-variant-numeric:tabular-nums}
16906
- .knobs-wt-rec:empty{display:none}
16907
- .knobs-ui .knobs-wt-tool[data-send]{color:var(--_k-fg-3)}
16908
- .knobs-ui .knobs-wt-tool[data-send][data-ready]{background:var(--_k-accent);color:var(--_k-bg)}
16909
- /* Back: in place over the bar for a few seconds after a note moves the page on. */
16910
- .knobs-ui .knobs-wt-back{position:absolute;left:8px;bottom:calc(100% + 8px);height:28px;padding:0 12px;border-radius:999px;background:var(--_k-bg);color:var(--_k-fg);
16911
- box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);font-size:calc(var(--kw-text) - 1.5px);font-weight:var(--_k-weight-label);cursor:pointer;
16912
- opacity:0;scale:.96;filter:blur(3px);pointer-events:none;transition:opacity .11s cubic-bezier(.4,0,1,1),scale .11s cubic-bezier(.4,0,1,1),filter .11s cubic-bezier(.4,0,1,1)}
16913
- .knobs-ui .knobs-wt-back[data-on]{opacity:1;scale:1;filter:none;pointer-events:auto;transition:opacity .15s cubic-bezier(.2,.8,.2,1),scale .15s cubic-bezier(.2,.8,.2,1),filter .15s cubic-bezier(.2,.8,.2,1)}
16914
- .knobs-ui .knobs-wt-back:hover{background:var(--_k-fill-selected)}
16915
-
16916
- /* ——— ink over a step's media ——— */
16917
- .knobs-wt-media,.knobs-wt-options{position:relative}
16918
- .knobs-wt-ink{position:absolute;inset:0;z-index:5;width:100%;height:100%;overflow:visible;pointer-events:none;touch-action:none}
16919
- .knobs-wt-ink[data-on]{pointer-events:auto;cursor:crosshair}
16920
- .knobs-wt-ink path{fill:none;stroke:var(--kw-ink);stroke-width:3px;vector-effect:non-scaling-stroke;stroke-linecap:round;stroke-linejoin:round}
16921
-
16922
- /* ——— full-window: the step over the page, its media filling it; its place held in the page ——— */
16923
- .knobs-wt-scrim{position:fixed;inset:0;z-index:30;background:color-mix(in srgb,var(--_k-bg-page) 82%,transparent);opacity:0;pointer-events:none}
16924
- .knobs-wt-scrim[data-on]{pointer-events:auto}
16925
- .knobs-wt-hold-card{flex:none}
16926
- .knobs-wt-step[data-full]{position:fixed;z-index:35;margin:0;overflow:hidden;overscroll-behavior:contain;padding-bottom:84px;box-shadow:var(--kw-card-ring),var(--_k-shadow-overlay);background:var(--_k-bg)}
16927
- .knobs-wt-step[data-full]>:not(.knobs-wt-media){flex:none}
16928
- .knobs-wt-step[data-full] .knobs-wt-notes{display:none}
16929
- .knobs-wt-step[data-full]>.knobs-wt-media{flex:1;min-height:0;grid-auto-rows:minmax(0,1fr)}
16930
- .knobs-wt-step[data-full] .knobs-wt-slot{display:flex;flex-direction:column;min-height:0}
16931
- .knobs-wt-step[data-full] .knobs-wt-body{flex:1;min-height:0}
16932
- .knobs-wt-step[data-full] .knobs-wt-fit{height:100%;transition:none}
16933
- .knobs-wt-step[data-full] .knobs-wt-frame{height:100%;aspect-ratio:auto}
16934
-
16935
- /* ——— a taste test: the options side by side, Kit's pick lit and the rest stepping back ——— */
16936
- .knobs-wt-options{display:grid;grid-template-columns:repeat(var(--kw-n,2),minmax(0,1fr));gap:10px}
16937
- @container knobs-wt-step (max-width:640px){.knobs-wt-options{grid-template-columns:minmax(0,1fr)}}
16938
- .knobs-wt-option{position:relative;display:flex;flex-direction:column;gap:8px;min-width:0;padding:8px;border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_ku-wash);
16939
- transition:background var(--kw-snappy-ms) var(--kw-snappy)}
16940
- .knobs-wt-option[data-picked]{background:color-mix(in srgb,var(--_k-accent) 12%,var(--_ku-wash))}
16941
- .knobs-wt-option>:not(.knobs-wt-pick){transition:opacity var(--kw-snappy-ms) var(--kw-snappy)}
16942
- .knobs-wt-options[data-has-pick] .knobs-wt-option:not([data-picked])>:not(.knobs-wt-pick){opacity:.55}
16943
- .knobs-ui .knobs-wt-pick{display:flex;align-items:center;gap:8px;width:100%;min-height:28px;padding:0 4px;border-radius:calc(var(--_k-radius) - 2px);cursor:pointer;color:var(--_k-fg-2);text-align:left}
16944
- .knobs-ui .knobs-wt-pick:is(:hover,:focus-visible){color:var(--_k-fg)}
16945
- .knobs-wt-letter{flex:none;font:var(--_k-weight-title) calc(var(--kw-text) - 1px)/1 var(--_k-font);color:var(--_k-fg)}
16946
- .knobs-wt-olabel{flex:1;min-width:0;font-weight:var(--_k-weight-label);color:var(--_k-fg)}
16947
- .knobs-wt-suggested{flex:none;font-size:calc(var(--kw-text) - 3px);color:var(--_k-fg-3)}
16948
- .knobs-wt-check{flex:none;display:flex;width:18px;height:18px;align-items:center;justify-content:center;border-radius:50%;background:var(--_k-fill);color:transparent;
16949
- transition:background var(--kw-snappy-ms) var(--kw-snappy),color var(--kw-snappy-ms) var(--kw-snappy)}
16950
- .knobs-wt-check svg{width:12px;height:12px}
16951
- .knobs-wt-option[data-picked] .knobs-wt-check{background:var(--_k-accent);color:var(--_k-bg)}
16952
- .knobs-wt-key{display:inline-flex;align-items:center;justify-content:center;min-width:15px;height:15px;padding:0 3px;border-radius:4px;background:var(--_k-fill);color:var(--_k-fg-3);
16953
- font:var(--_k-weight-label) calc(var(--kw-text) - 4px)/1 var(--_k-font);font-variant-numeric:tabular-nums;opacity:.5;transition:opacity .15s}
16954
- .knobs-wt-step[data-current] .knobs-wt-key{opacity:1}
16955
- .knobs-wt-ocap{color:var(--_k-fg-2);font-size:calc(var(--kw-text) - 1px);padding:0 4px}
16971
+ init_icons();
16972
+ init_look();
16973
+ init_selection_pill();
16956
16974
 
16957
- /* ——— the rail: progress, contents, export ——— */
16958
- .knobs-wt-rail{position:sticky;top:16px;display:flex;flex-direction:column;gap:14px;max-height:calc(100vh - 32px);min-height:0;font-size:calc(var(--kw-text) - 1.5px)}
16959
- .knobs-wt-progress{display:flex;flex-direction:column;gap:7px}
16960
- .knobs-wt-count{display:flex;align-items:baseline;justify-content:space-between;color:var(--_k-fg-2);font-variant-numeric:tabular-nums}
16961
- .knobs-wt-count b{color:var(--_k-fg);font-weight:var(--_k-weight-label)}
16962
- .knobs-wt-bar{position:relative;height:4px;border-radius:2px;overflow:hidden;background:var(--_k-fill)}
16963
- .knobs-wt-fill{position:absolute;inset:0;border-radius:inherit;background:var(--_k-accent);transform-origin:0 50%;transform:scaleX(var(--kw-done,0))}
16964
- .knobs-walkthrough[data-settled] .knobs-wt-fill{transition:transform var(--kw-calm-ms) var(--kw-calm)}
16965
- /* The contents: no box, one text spine for sections and steps (the steps' dots hang in the
16966
- gutter left of it), and one pill behind the current step that springs between rows. */
16967
- .knobs-wt-toc{position:relative;flex:1 1 auto;min-height:0;overflow:auto;scrollbar-width:none;margin:0 -8px;padding:0 0 8px}
16968
- .knobs-wt-toc::-webkit-scrollbar{display:none}
16969
- .knobs-wt-toc-section{display:block;padding:12px 8px 4px 26px;color:var(--_k-fg-3);font-size:calc(var(--kw-text) - 3px);font-weight:var(--_k-weight-label);white-space:nowrap;overflow:hidden;
16970
- -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent)}
16971
- .knobs-wt-toc-section:first-of-type{padding-top:2px}
16972
- .knobs-wt-toc-mark{position:absolute;left:0;right:0;top:0;height:0;border-radius:calc(var(--_k-radius) - 1px);background:var(--_ku-wash-hi);pointer-events:none;opacity:0;will-change:transform}
16973
- .knobs-wt-toc-mark[data-on]{opacity:1}
16974
- .knobs-walkthrough[data-settled] .knobs-wt-toc-mark{transition:opacity .15s}
16975
- .knobs-ui .knobs-wt-toc-step{position:relative;display:flex;align-items:center;width:100%;text-align:left;height:26px;padding:0 8px 0 26px;color:var(--_k-fg-2);cursor:pointer;white-space:nowrap}
16976
- .knobs-ui .knobs-wt-toc-step:is(:hover,:focus-visible){color:var(--_k-fg)}
16977
- .knobs-wt-toc-step[aria-current="step"]{color:var(--_k-fg)}
16978
- .knobs-wt-toc-label{flex:1;min-width:0;overflow:hidden;-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent)}
16979
- /* A step's state: an open ring, a filled dot once answered, nothing to answer a faint ring. */
16980
- .knobs-wt-toc-glyph{--kw-tone:var(--_k-fg-3);position:absolute;left:11px;top:50%;width:7px;height:7px;margin-top:-3.5px;border-radius:50%;box-shadow:inset 0 0 0 1.5px var(--kw-tone);
16981
- transition:background var(--kw-snappy-ms) var(--kw-snappy),box-shadow var(--kw-snappy-ms) var(--kw-snappy)}
16982
- .knobs-wt-toc-glyph[data-tone="accent"]{--kw-tone:var(--_k-accent)}
16983
- .knobs-wt-toc-glyph[data-done]{background:var(--kw-tone)}
16984
- .knobs-wt-toc-glyph[data-tone="none"]{box-shadow:inset 0 0 0 1px var(--_k-border)}
16985
- .knobs-wt-actions{display:flex;flex-wrap:wrap;gap:4px}
16986
- .knobs-ui .knobs-wt-btn{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 9px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:pointer;white-space:nowrap;
16987
- transition:background .15s,color .15s}
16988
- .knobs-ui .knobs-wt-btn:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
16989
- .knobs-ui .knobs-wt-btn[data-primary]{background:var(--_k-fill-selected);color:var(--_k-fg)}
16990
- .knobs-ui .knobs-wt-btn[data-primary]:is(:hover,:focus-visible){background:color-mix(in srgb,var(--_k-fg) 16%,transparent)}
16991
- .knobs-wt-keys{color:var(--_k-fg-3);font-size:calc(var(--kw-text) - 3px);line-height:1.6}
16992
- .knobs-wt-keys kbd{font:inherit;color:var(--_k-fg-2)}
16993
- /* Narrow: the rail becomes a bar along the top, with the progress and the export only. */
16994
- @container knobs-wt (max-width:880px){
16995
- .knobs-wt-rail{z-index:4;flex-direction:row;align-items:center;gap:12px;max-height:none;margin:0 calc(var(--kw-pad) * -1) 20px;padding:8px var(--kw-pad);top:0;background:var(--_k-bg-page)}
16996
- .knobs-wt-progress{flex:1}.knobs-wt-toc,.knobs-wt-keys{display:none}
16997
- .knobs-wt-step,.knobs-wt-shead{scroll-margin-top:60px}
16998
- }
16999
-
17000
- /* ——— media slots ——— */
17001
- .knobs-ui.knobs-wt-slot{position:relative;min-width:0;container:knobs-wt-slot / inline-size;color:var(--_k-fg);font:var(--_k-weight) var(--k-walkthrough-text,14px)/1.5 var(--_k-font);
17002
- --kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info))}
17003
- .knobs-wt-slot-label{display:block;margin:0 0 6px 2px;font:var(--_k-weight-label) calc(var(--k-walkthrough-text,14px) - 3px)/1.3 var(--_k-font);color:var(--_k-fg-2);letter-spacing:.04em;text-transform:uppercase}
17004
- /* A compare is exactly as tall as its pictures at this width, so there's no letterbox: the two
17005
- sides edge to edge (side) or one (overlaid), plus its mode bar, the timeline and their two gaps
17006
- (92 px), and the frame strip with its gap (158 px). */
17007
- .knobs-wt-fit{width:100%;height:calc(100cqw / 2 / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
17008
- .knobs-wt-fit[data-overlay]{height:calc(100cqw / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
17009
- .knobs-wt-fit[data-strip]{--kw-chrome:158px}
17010
- .knobs-wt-fit[data-settled]{transition:height var(--k-wt-calm-ms,${calm.duration}ms) ${calm.easing}}
17011
- .knobs-wt-frame{position:relative;width:100%;overflow:hidden;border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_k-fill);aspect-ratio:var(--kw-aspect,16/9)}
17012
- .knobs-wt-frame>:is(video,img){position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:contain;margin:0;border:0}
17013
- .knobs-wt-frame[data-natural]{aspect-ratio:auto}
17014
- .knobs-wt-frame[data-natural]>img{position:static;height:auto}
17015
- .knobs-wt-code{margin:0;padding:12px 14px;max-height:420px;overflow:auto;border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_k-fill);color:var(--_k-fg);
17016
- font:calc(var(--k-walkthrough-text,14px) - 2px)/1.55 var(--_k-mono);white-space:pre;tab-size:2;scrollbar-width:thin}
17017
- .knobs-wt-code[data-terminal]{white-space:pre-wrap;overflow-wrap:anywhere}
17018
- .knobs-wt-prompt{color:var(--_k-fg-3);user-select:none;-webkit-user-select:none}
17019
- .knobs-wt-cmd{color:var(--_k-fg);font-weight:var(--_k-weight-label)}
17020
- .knobs-wt-out{color:var(--_k-fg-2)}
17021
- .knobs-wt-metric{display:flex;flex-direction:column;gap:8px;padding:12px 14px;border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_k-fill)}
17022
- .knobs-wt-mlabel{color:var(--_k-fg-2);font-size:calc(var(--k-walkthrough-text,14px) - 2px)}
17023
- .knobs-wt-mvalue{display:flex;align-items:baseline;flex-wrap:wrap;gap:8px;font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
17024
- .knobs-wt-mfrom{color:var(--_k-fg-3);font-size:calc(var(--k-walkthrough-text,14px) + 2px)}
17025
- .knobs-wt-mto{font-size:calc(var(--k-walkthrough-text,14px) + 12px);font-weight:var(--_k-weight-title);line-height:1}
17026
- .knobs-wt-mof{color:var(--_k-fg-3)}
17027
- .knobs-wt-mdelta{--kw-tone:var(--_k-fg-2);padding:1px 7px;border-radius:999px;font-size:calc(var(--k-walkthrough-text,14px) - 3px);font-weight:var(--_k-weight-label);color:var(--kw-tone);background:color-mix(in srgb,var(--kw-tone) 14%,transparent)}
17028
- .knobs-wt-mdelta[data-tone="good"]{--kw-tone:var(--kw-good)}
17029
- .knobs-wt-mdelta[data-tone="bad"]{--kw-tone:var(--_k-danger)}
17030
- .knobs-wt-mtrack{position:relative;height:4px;border-radius:2px;background:color-mix(in srgb,var(--_k-fg) 8%,transparent)}
17031
- .knobs-wt-mtrack>span{position:absolute;top:0;bottom:0;left:0;border-radius:inherit}
17032
- .knobs-wt-mtrack>.knobs-wt-mafter{background:var(--_k-accent)}
17033
- .knobs-wt-mtrack>.knobs-wt-mbefore{background:color-mix(in srgb,var(--_k-fg) 30%,transparent)}
17034
- .knobs-wt-callout{--kw-tone:var(--kw-info);display:flex;gap:10px;padding:10px 14px;border-radius:calc(var(--_k-radius-panel) - 4px);background:color-mix(in srgb,var(--kw-tone) 11%,transparent);color:var(--_k-fg)}
17035
- .knobs-wt-callout::before{content:"";flex:none;width:6px;height:6px;margin-top:.62em;border-radius:50%;background:var(--kw-tone)}
17036
- .knobs-wt-callout[data-tone="good"]{--kw-tone:var(--kw-good)}
17037
- .knobs-wt-callout[data-tone="warn"]{--kw-tone:var(--_k-warn)}
17038
- .knobs-wt-callout[data-tone="bad"]{--kw-tone:var(--_k-danger)}
17039
- .knobs-wt-text{max-width:var(--k-walkthrough-measure,68ch);color:var(--_k-fg-2)}
17040
- .knobs-wt-hold{border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_k-fill)}
17041
- .knobs-wt-failed{display:flex;align-items:center;justify-content:center;min-height:80px;color:var(--_k-fg-3);font-size:calc(var(--k-walkthrough-text,14px) - 2px)}
17042
-
17043
- /* ——— data blocks: history, stack, table, chapters. On the step's card, so no card of their own:
17044
- hairlines and fills only. Tones read the status tokens; hover fades are the panel's own. ——— */
17045
- :where(.knobs-wt-history,.knobs-wt-stack,.knobs-wt-table,.knobs-wt-chapters){--kw-t:var(--k-walkthrough-text,14px);--kw-line:calc(var(--kw-t) + 6px);--kw-rail:var(--_k-border-strong)}
17046
- :where(.knobs-wt-slot) [data-tone="good"]{--kw-tone:var(--kw-good)}
17047
- :where(.knobs-wt-slot) [data-tone="warn"]{--kw-tone:var(--_k-warn)}
17048
- :where(.knobs-wt-slot) [data-tone="bad"]{--kw-tone:var(--_k-danger)}
17049
- :where(.knobs-wt-slot) [data-tone="info"]{--kw-tone:var(--kw-info)}
17050
- :where(.knobs-wt-slot) [data-tone="dim"]{--kw-tone:var(--_k-fg-3)}
17051
- .knobs-wt-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
17052
- .knobs-ui .knobs-wt-tag{display:inline-flex;align-items:center;height:20px;padding:0 6px;border-radius:5px;background:var(--_k-fill);color:var(--_k-fg-2);font:var(--_k-weight) calc(var(--kw-t) - 3px)/1 var(--_k-mono);
17053
- text-decoration:none;white-space:nowrap;transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}
17054
- .knobs-ui a.knobs-wt-tag:is(:hover,:focus-visible){background:var(--_k-fill-selected);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
17055
-
17056
- /* A history: dates down the left, a spine of tone dots joined by a hairline (broken around each
17057
- dot), the event beside it. Inferred events take a ring for a dot and a dashed tag. */
17058
- .knobs-wt-history{--kw-hy:calc(5px + var(--kw-line) / 2);display:flex;flex-direction:column;gap:18px;font-size:calc(var(--kw-t) - 1px);line-height:var(--kw-line);color:var(--_k-fg)}
17059
- .knobs-wt-hgroup{display:flex;flex-direction:column;gap:6px;min-width:0}
17060
- .knobs-wt-hhead{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px}
17061
- .knobs-ui .knobs-wt-hheading{margin:0;font:var(--_k-weight-label) calc(var(--kw-t) - 3px)/1.3 var(--_k-font);letter-spacing:.04em;text-transform:uppercase;color:var(--_k-fg-2)}
17062
- .knobs-wt-hsum{color:var(--_k-fg-3);font-size:calc(var(--kw-t) - 2px);font-variant-numeric:tabular-nums}
17063
- .knobs-wt-hlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
17064
- .knobs-wt-hev{display:grid;grid-template-columns:var(--kw-hw,11ch) 14px minmax(0,1fr);grid-template-areas:"when spine main";column-gap:10px;align-items:baseline;padding:5px 0}
17065
- .knobs-wt-hwhen{grid-area:when;display:flex;flex-direction:column;min-width:0;font:var(--_k-weight) calc(var(--kw-t) - 2px)/var(--kw-line) var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-2)}
17066
- .knobs-wt-hage{color:var(--_k-fg-3);font-size:calc(var(--kw-t) - 3px);line-height:1.35}
17067
- .knobs-wt-hspine{grid-area:spine;position:relative;align-self:stretch;margin-block:-5px}
17068
- .knobs-wt-hspine::before,.knobs-wt-hspine::after{content:"";position:absolute;left:6.5px;width:1px;background:var(--kw-rail)}
17069
- .knobs-wt-hspine::before{top:0;height:calc(var(--kw-hy) - 6px)}
17070
- .knobs-wt-hspine::after{top:calc(var(--kw-hy) + 6px);bottom:0}
17071
- .knobs-wt-hev:first-child .knobs-wt-hspine::before,.knobs-wt-hev:last-child .knobs-wt-hspine::after{content:none}
17072
- .knobs-wt-hdot{position:absolute;left:3.5px;top:calc(var(--kw-hy) - 3.5px);width:7px;height:7px;border-radius:50%;background:var(--kw-tone,var(--_k-fg-3))}
17073
- .knobs-wt-hev[data-type="release"] .knobs-wt-hdot{border-radius:1.5px;rotate:45deg;scale:.9}
17074
- .knobs-wt-hev[data-type="audit"] .knobs-wt-hdot{border-radius:1.5px}
17075
- .knobs-wt-hev[data-provenance="inferred"] .knobs-wt-hdot{background:transparent;box-shadow:inset 0 0 0 1.5px var(--kw-tone,var(--_k-fg-3))}
17076
- .knobs-wt-hmain{grid-area:main;display:flex;flex-direction:column;gap:2px;min-width:0}
17077
- .knobs-wt-htitle{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px;font-weight:var(--_k-weight-label);color:var(--_k-fg)}
17078
- .knobs-wt-prov{padding:0 6px;border-radius:5px;background:var(--_k-fill);color:var(--_k-fg-3);font:var(--_k-weight) calc(var(--kw-t) - 3.5px)/18px var(--_k-mono);white-space:nowrap}
17079
- .knobs-wt-hev[data-provenance="inferred"] .knobs-wt-prov{background:transparent;color:var(--_k-warn);outline:1px dashed color-mix(in oklch,var(--_k-warn) 50%,transparent);outline-offset:-1px}
17080
- .knobs-ui .knobs-wt-hdetail{margin:0;max-width:var(--k-walkthrough-measure,68ch);color:var(--_k-fg-2)}
17081
- @container knobs-wt-slot (max-width:520px){
17082
- .knobs-wt-hev{grid-template-columns:14px minmax(0,1fr);grid-template-areas:"spine when" "spine main"}
17083
- .knobs-wt-hwhen{flex-direction:row;flex-wrap:wrap;align-items:baseline;column-gap:8px}
17084
- }
17085
-
17086
- /* A stack: the trunk at the top, each PR hanging off the branch it merges into, rails drawn as a
17087
- git graph (solid, so where two meet they don't darken). A PR with a step goes there on a click. */
17088
- .knobs-wt-stack{--kw-spt:7px;--kw-spb:7px;--kw-sy:calc(var(--kw-spt) + var(--kw-line) / 2);display:flex;flex-direction:column;font-size:calc(var(--kw-t) - 1px);line-height:var(--kw-line);color:var(--_k-fg)}
17089
- .knobs-wt-srow{display:grid;grid-template-columns:auto minmax(0,1fr) auto;column-gap:10px;align-items:start;margin:0 -8px;padding:var(--kw-spt) 8px var(--kw-spb);
17090
- border-radius:max(0px,calc(var(--_k-radius-panel) - 8px));transition:background calc(var(--_k-hover-out) * 1ms)}
17091
- .knobs-wt-srow[data-trunk]{--kw-spb:0px;grid-template-columns:auto minmax(0,1fr)}
17092
- .knobs-wt-srow[data-go]{cursor:pointer}
17093
- .knobs-wt-srow[data-go]:hover{background:var(--_ku-wash);transition-duration:calc(var(--_k-hover-in) * 1ms)}
17094
- .knobs-wt-lanes{display:flex;align-self:stretch;margin-block:calc(var(--kw-spt) * -1) calc(var(--kw-spb) * -1)}
17095
- .knobs-wt-lane{position:relative;flex:none;width:16px}
17096
- .knobs-wt-lane:is([data-rail="pass"],[data-rail="tee"],[data-down])::before{content:"";position:absolute;left:7.5px;top:0;bottom:0;width:1px;background:var(--kw-rail)}
17097
- .knobs-wt-lane[data-down]::before{top:calc(var(--kw-sy) + 3.5px)}
17098
- .knobs-wt-lane:is([data-rail="tee"],[data-rail="end"])::after{content:"";position:absolute;left:7.5px;top:0;width:13px;height:calc(var(--kw-sy) + .5px);border:solid var(--kw-rail);border-width:0 0 1px 1px;border-bottom-left-radius:6px}
17099
- .knobs-wt-lane[data-rail="node"]::after{content:"";position:absolute;left:4.5px;top:calc(var(--kw-sy) - 3.5px);width:7px;height:7px;border-radius:50%;background:var(--_k-fg-3)}
17100
- .knobs-wt-srow[data-trunk] .knobs-wt-lane[data-rail="node"]::after{background:var(--_k-fg-2)}
17101
- .knobs-wt-trunk{font:var(--_k-weight-label) calc(var(--kw-t) - 2px)/var(--kw-line) var(--_k-mono);color:var(--_k-fg-2)}
17102
- .knobs-wt-smain{display:flex;flex-direction:column;gap:1px;min-width:0}
17103
- .knobs-wt-sline{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 8px}
17104
- .knobs-wt-sord{font:var(--_k-weight) calc(var(--kw-t) - 2px)/var(--kw-line) var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-3);white-space:nowrap}
17105
- .knobs-ui .knobs-wt-spr{font:var(--_k-weight-label) calc(var(--kw-t) - 1.5px)/var(--kw-line) var(--_k-mono);color:var(--_k-fg-2);text-decoration:none;white-space:nowrap}
17106
- .knobs-ui a.knobs-wt-spr:is(:hover,:focus-visible){color:var(--_k-fg);text-decoration:underline}
17107
- .knobs-ui .knobs-wt-stitle{font-weight:var(--_k-weight-label);line-height:var(--kw-line);color:var(--_k-fg);text-align:left}
17108
- .knobs-ui button.knobs-wt-stitle{cursor:pointer;text-decoration:underline;text-decoration-color:transparent;text-underline-offset:3px;transition:text-decoration-color calc(var(--_k-hover-out) * 1ms)}
17109
- .knobs-ui :is(.knobs-wt-srow[data-go]:hover button.knobs-wt-stitle,button.knobs-wt-stitle:focus-visible){text-decoration-color:color-mix(in oklch,currentColor 45%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}
17110
- .knobs-ui .knobs-wt-ssummary{margin:0;max-width:var(--k-walkthrough-measure,68ch);color:var(--_k-fg-2)}
17111
- .knobs-wt-smeta{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 12px;font:var(--_k-weight) calc(var(--kw-t) - 2px)/var(--kw-line) var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-3)}
17112
- .knobs-wt-delta{display:inline-flex;gap:6px}
17113
- .knobs-wt-add{color:var(--_k-diff-add)}
17114
- .knobs-wt-del{color:var(--_k-diff-del)}
17115
- .knobs-wt-sstatus{margin-top:calc((var(--kw-line) - 20px) / 2)}
17116
- .knobs-wt-stack[data-flat] .knobs-wt-srow{grid-template-columns:minmax(0,1fr) auto}
17117
- .knobs-wt-stack[data-flat] .knobs-wt-srow+.knobs-wt-srow{box-shadow:inset 0 1px 0 var(--_k-border)}
17118
- @container knobs-wt-slot (max-width:520px){
17119
- .knobs-wt-srow:not([data-trunk]){grid-template-columns:auto minmax(0,1fr)}
17120
- .knobs-wt-stack[data-flat] .knobs-wt-srow{grid-template-columns:minmax(0,1fr)}
17121
- .knobs-wt-sstatus{grid-column:-2;justify-self:start;margin-top:4px}
17122
- }
17123
-
17124
- /* A table: a real one, its head stuck while it scrolls, both ways; edges fade where there's more
17125
- (the top one under the head), and thin bars show on hover. */
17126
- @property --kw-fade-l{syntax:"<number>";inherits:false;initial-value:1}
17127
- @property --kw-fade-r{syntax:"<number>";inherits:false;initial-value:1}
17128
- @keyframes kw-fade-l{from{--kw-fade-l:1}to{--kw-fade-l:0}}
17129
- @keyframes kw-fade-r{from{--kw-fade-r:0}to{--kw-fade-r:1}}
17130
- .knobs-wt-table{max-height:520px;overflow:auto;overscroll-behavior-x:contain;outline:none;scrollbar-width:thin;scrollbar-color:transparent transparent;
17131
- transition:scrollbar-color calc(var(--_k-hover-out) * 1ms)}
17132
- .knobs-wt-table:is(:hover,:focus-visible){scrollbar-color:color-mix(in oklch,var(--_k-fg) 28%,transparent) transparent;transition-duration:calc(var(--_k-hover-in) * 1ms)}
17133
- .knobs-wt-table:focus-visible{box-shadow:inset 0 0 0 2px var(--_k-focus);border-radius:calc(var(--_k-radius-panel) - 4px)}
17134
- @supports (animation-timeline:scroll()){
17135
- .knobs-wt-table{animation:knobs-fade-a linear both,knobs-fade-b linear both,kw-fade-l linear both,kw-fade-r linear both;animation-duration:auto;
17136
- animation-timeline:scroll(self block),scroll(self block),scroll(self inline),scroll(self inline);animation-range:0 24px,calc(100% - 24px) 100%,0 8px,calc(100% - 8px) 100%;
17137
- --kw-table-mask-y:linear-gradient(to bottom,#000 var(--kw-thead,0px),rgb(0 0 0/var(--knobs-fade-a)) var(--kw-thead,0px),#000 calc(var(--kw-thead,0px) + 20px),#000 calc(100% - 20px),rgb(0 0 0/var(--knobs-fade-b)));
17138
- --kw-table-mask-x:linear-gradient(to right,rgb(0 0 0/var(--kw-fade-l)),#000 24px,#000 calc(100% - 24px),rgb(0 0 0/var(--kw-fade-r)));
17139
- -webkit-mask-image:var(--kw-table-mask-y),var(--kw-table-mask-x);mask-image:var(--kw-table-mask-y),var(--kw-table-mask-x);-webkit-mask-composite:source-in;mask-composite:intersect}
17140
- }
17141
- .knobs-wt-table table{width:100%;margin:0;border-collapse:separate;border-spacing:0;font-size:calc(var(--kw-t) - 1.5px);line-height:1.45;font-variant-numeric:tabular-nums;color:var(--_k-fg-2)}
17142
- .knobs-wt-table caption{caption-side:bottom;padding-top:8px;text-align:left;color:var(--_k-fg-3);font-size:calc(var(--kw-t) - 2px)}
17143
- .knobs-wt-table th{position:sticky;top:0;padding:0 12px 7px;text-align:left;vertical-align:bottom;font-weight:var(--_k-weight-label);font-size:calc(var(--kw-t) - 2.5px);color:var(--_k-fg-3);white-space:nowrap;
17144
- background:var(--_k-pane-bg);box-shadow:inset 0 -1px 0 var(--_k-border)}
17145
- .knobs-wt-table td{padding:8px 12px;vertical-align:baseline;box-shadow:inset 0 -1px 0 var(--_k-border)}
17146
- .knobs-wt-table tr:last-child td{box-shadow:none}
17147
- .knobs-wt-table :is(th,td):first-child{padding-left:0}
17148
- .knobs-wt-table :is(th,td):last-child{padding-right:0}
17149
- .knobs-wt-table td:first-child{color:var(--_k-fg)}
17150
- .knobs-wt-table [data-align="right"]{text-align:right}
17151
- .knobs-wt-table [data-align="center"]{text-align:center}
17152
- .knobs-wt-table td[data-mono]{font-family:var(--_k-mono);font-size:calc(var(--kw-t) - 2.5px);color:var(--_k-fg);white-space:nowrap}
17153
- .knobs-wt-table td[data-long]{min-width:24ch}
17154
- .knobs-wt-table td>.knobs-wt-steplink{margin-left:4px}
17155
- .knobs-ui .knobs-wt-table a.knobs-wt-chip{text-decoration:none}
17156
- .knobs-wt-ctone{color:var(--kw-tone)}
17157
-
17158
- /* Chapters: timestamp pills under a video; one highlight springs to the one playing. */
17159
- .knobs-wt-chapters{position:relative;display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}
17160
- .knobs-wt-chapter-mark{position:absolute;left:0;top:0;width:0;border-radius:calc(var(--_k-radius) + 2px);background:var(--_k-fill-selected);opacity:0;pointer-events:none;will-change:transform;
17161
- transition:opacity calc(var(--_k-motion-snappy-ms) * 1ms)}
17162
- .knobs-wt-chapter-mark[data-on]{opacity:1}
17163
- .knobs-ui .knobs-wt-chapter{position:relative;display:inline-flex;align-items:baseline;gap:6px;height:26px;padding:0 10px 0 8px;border-radius:calc(var(--_k-radius) + 2px);color:var(--_k-fg-2);
17164
- font-size:calc(var(--kw-t) - 2px);line-height:26px;white-space:nowrap;cursor:pointer;transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}
17165
- .knobs-ui .knobs-wt-chapter:hover{background:var(--_ku-wash);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
17166
- .knobs-ui .knobs-wt-chapter[aria-current]{background:transparent;color:var(--_k-fg)}
17167
- .knobs-ui .knobs-wt-chapter:focus-visible{box-shadow:inset 0 0 0 2px var(--_k-focus)}
17168
- .knobs-wt-chapter-time{font:var(--_k-weight) calc(var(--kw-t) - 3px)/1 var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-3)}
17169
- .knobs-wt-chapter[aria-current] .knobs-wt-chapter-time{color:var(--_k-fg-2)}
17170
- @media(prefers-reduced-motion:reduce){:is(.knobs-walkthrough,.knobs-wt-slot) :where(*,::before,::after){transition-duration:.15s!important;transition-property:opacity,background,color!important}}
17171
- `;
17172
-
17173
- // src/ui/walkthrough-blocks.ts
17174
- init_motion();
17175
- init_look();
17176
-
17177
- // src/ui/walkthrough-model.ts
17178
- var DEFAULT_VERDICTS = ["ship", "change", "unsure"];
17179
- function slug(text) {
17180
- return text.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/^(.{0,48})-.*$/, (m, head) => m.length > 48 && head ? head : m).slice(0, 48) || "item";
16975
+ // src/ui/walkthrough-model.ts
16976
+ var DEFAULT_VERDICTS = ["ship", "change", "unsure"];
16977
+ function slug(text) {
16978
+ return text.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/^(.{0,48})-.*$/, (m, head) => m.length > 48 && head ? head : m).slice(0, 48) || "item";
17181
16979
  }
17182
16980
  function uniqueIds() {
17183
16981
  const seen = new Map;
@@ -17379,7 +17177,15 @@ function fillTable(m) {
17379
17177
  out[k] = fillCell(v);
17380
17178
  return out;
17381
17179
  });
17382
- return { kind: "table", columns, rows, ...m.caption ? { caption: m.caption } : {} };
17180
+ const primary = columns.find((c) => c.key === m.primary)?.key ?? columns[0]?.key ?? "";
17181
+ return {
17182
+ kind: "table",
17183
+ columns,
17184
+ rows,
17185
+ ...m.caption ? { caption: m.caption } : {},
17186
+ stack: m.stack ?? "auto",
17187
+ primary
17188
+ };
17383
17189
  }
17384
17190
  function fillChapters(chapters) {
17385
17191
  const id = uniqueIds();
@@ -17452,6 +17258,8 @@ function normalizeWalkthrough(doc) {
17452
17258
  ...step.caption ? { caption: step.caption } : {},
17453
17259
  ...step.question ? { question: step.question } : {},
17454
17260
  media: list.map((m) => fillMedia(m, doc.base)),
17261
+ columns: Math.max(1, Math.min(3, Math.round(step.columns ?? 1))),
17262
+ ...step.more?.trim() && list.length > 1 ? { more: step.more.trim() } : {},
17455
17263
  options,
17456
17264
  ask
17457
17265
  };
@@ -17463,6 +17271,7 @@ function normalizeWalkthrough(doc) {
17463
17271
  id,
17464
17272
  title: s.title,
17465
17273
  ...link ? { link } : {},
17274
+ ...typeof s.kicker === "string" && s.kicker.trim() ? { kicker: s.kicker.trim() } : {},
17466
17275
  ...s.status ? { status: s.status } : {},
17467
17276
  ...s.summary ? { summary: s.summary } : {},
17468
17277
  steps: own
@@ -17473,14 +17282,25 @@ function normalizeWalkthrough(doc) {
17473
17282
  value
17474
17283
  }));
17475
17284
  const stepRef = (ref) => findStep(steps, ref)?.key ?? null;
17285
+ const extra = (x) => ({
17286
+ ...x.status?.trim() ? { status: x.status.trim() } : {},
17287
+ trail: (x.trail ?? []).filter((t) => typeof t === "string" && t.trim())
17288
+ });
17476
17289
  const feed = sortFeed([
17477
- ...(doc.updates ?? []).map((u) => ({ kind: "update", at: parseAt(u.at), text: u.text, step: stepRef(u.step) })),
17290
+ ...(doc.updates ?? []).map((u) => ({
17291
+ kind: "update",
17292
+ at: parseAt(u.at),
17293
+ text: u.text,
17294
+ step: stepRef(u.step),
17295
+ ...extra(u)
17296
+ })),
17478
17297
  ...(doc.qa ?? []).map((x) => ({
17479
17298
  kind: "qa",
17480
17299
  at: parseAt(x.at),
17481
17300
  text: x.a,
17482
17301
  q: x.q,
17483
- step: stepRef(x.step)
17302
+ step: stepRef(x.step),
17303
+ ...extra(x)
17484
17304
  }))
17485
17305
  ]);
17486
17306
  return {
@@ -17493,10 +17313,32 @@ function normalizeWalkthrough(doc) {
17493
17313
  id: slug(v),
17494
17314
  label: label(v)
17495
17315
  })),
17496
- sections,
17316
+ sections: withKickers(sections, doc.sections, doc.kickers),
17497
17317
  steps
17498
17318
  };
17499
17319
  }
17320
+ var isPullRequest = (href) => /\/(?:pull|merge_requests)\/\d+(?:[/?#]|$)/.test(href);
17321
+ function withKickers(sections, source, kickers = "pr") {
17322
+ if (kickers === false)
17323
+ return [...sections];
17324
+ const noun = typeof kickers === "object" ? kickers.noun.trim() : "PR";
17325
+ const counted = typeof kickers === "object" ? sections : sections.filter((s) => s.link && isPullRequest(s.link.href));
17326
+ return sections.map((s, i) => {
17327
+ const own = source[i]?.kicker;
17328
+ if (own !== undefined || s.kicker)
17329
+ return s;
17330
+ const at = counted.indexOf(s);
17331
+ return at < 0 ? s : { ...s, kicker: `${noun} ${at + 1} of ${counted.length}`.trim() };
17332
+ });
17333
+ }
17334
+ function tableStacks(stack, room, natural) {
17335
+ if (stack !== "auto")
17336
+ return stack === "always";
17337
+ return room > 0 && natural > room + 1;
17338
+ }
17339
+ function noteBarReserve(bar, inset, gap) {
17340
+ return Math.max(0, Math.ceil(bar)) + inset + gap;
17341
+ }
17500
17342
  function parseAt(at) {
17501
17343
  if (!at)
17502
17344
  return null;
@@ -17781,6 +17623,9 @@ function feedbackMarkdown(fb, verdicts) {
17781
17623
  `;
17782
17624
  }
17783
17625
  var escapeHtml = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
17626
+ function plainMarkdown(text) {
17627
+ return text.replace(/\[([^\]\n]+)\]\([^)\s]+\)/g, "$1").replace(/`([^`\n]+)`/g, "$1").replace(/\*\*([^*\n]+)\*\*/g, "$1").replace(/(^|[^*\w])\*([^*\n]+)\*(?!\w)/g, "$1$2").replace(/\s+/g, " ").trim();
17628
+ }
17784
17629
  function inlineMarkdown(text) {
17785
17630
  const codes = [];
17786
17631
  let s = text.replace(/`([^`\n]+)`/g, (_, c) => {
@@ -17801,6 +17646,10 @@ function inlineMarkdown(text) {
17801
17646
  }
17802
17647
 
17803
17648
  // src/ui/walkthrough-blocks.ts
17649
+ var GLYPH = {
17650
+ go: icon("caretRight", { small: true }),
17651
+ caret: icon("caretRight", { small: true })
17652
+ };
17804
17653
  var el = (doc, tag, cls) => {
17805
17654
  const n = doc.createElement(tag);
17806
17655
  if (cls)
@@ -17830,9 +17679,10 @@ function stepButton(doc, ref, steps) {
17830
17679
  const s = ref && steps?.find(ref);
17831
17680
  if (!s || !steps)
17832
17681
  return null;
17833
- const b = text(doc, "button", "knobs-wt-steplink", s.number);
17682
+ const b = el(doc, "button", "knobs-wt-steplink");
17683
+ b.innerHTML = GLYPH.go;
17834
17684
  b.type = "button";
17835
- b.title = `Go to ${s.number} ${s.title}`;
17685
+ b.title = `Go to “${s.title}”`;
17836
17686
  b.setAttribute("aria-label", b.title);
17837
17687
  b.addEventListener("click", (e) => {
17838
17688
  e.stopPropagation();
@@ -17957,7 +17807,7 @@ function mountStack(host, m, steps) {
17957
17807
  if (target && steps) {
17958
17808
  const title = text(doc, "button", "knobs-wt-stitle", it.title);
17959
17809
  title.type = "button";
17960
- title.title = `Go to ${target.number} ${target.title}`;
17810
+ title.title = `Go to “${target.title}”`;
17961
17811
  title.addEventListener("click", (e) => {
17962
17812
  e.stopPropagation();
17963
17813
  steps.go(target.key);
@@ -18012,9 +17862,38 @@ function delta(doc, d) {
18012
17862
  s.setAttribute("aria-label", `${d.add} added, ${d.del} removed`);
18013
17863
  return s;
18014
17864
  }
17865
+ function cellContent(doc, into, cell, steps) {
17866
+ let content;
17867
+ if (cell.badge) {
17868
+ content = link(doc, "knobs-wt-chip", cell.text, cell.href);
17869
+ content.dataset.tone = cell.badge;
17870
+ } else {
17871
+ const inner = cell.code ? text(doc, "code", "", cell.text) : null;
17872
+ if (cell.href) {
17873
+ content = link(doc, "knobs-wt-clink", inner ? "" : cell.text, cell.href);
17874
+ if (inner)
17875
+ content.append(inner);
17876
+ } else
17877
+ content = inner ?? text(doc, "span", "", cell.text);
17878
+ if (cell.tone) {
17879
+ content.classList.add("knobs-wt-ctone");
17880
+ content.dataset.tone = cell.tone;
17881
+ }
17882
+ }
17883
+ into.append(content);
17884
+ const go = stepButton(doc, cell.step, steps);
17885
+ if (go)
17886
+ into.append(go);
17887
+ }
17888
+ var cellAttrs = (td, c) => {
17889
+ td.dataset.align = c.align;
17890
+ if (c.mono)
17891
+ td.dataset.mono = "";
17892
+ };
18015
17893
  function mountTable(host, m, steps) {
18016
17894
  const doc = host.ownerDocument;
18017
17895
  const win = doc.defaultView;
17896
+ const box = el(doc, "div", "knobs-wt-tablebox");
18018
17897
  const scroller = el(doc, "div", "knobs-wt-table");
18019
17898
  const table = el(doc, "table");
18020
17899
  if (m.caption)
@@ -18033,34 +17912,12 @@ function mountTable(host, m, steps) {
18033
17912
  const tr = el(doc, "tr");
18034
17913
  for (const c of m.columns) {
18035
17914
  const td = el(doc, "td");
18036
- td.dataset.align = c.align;
18037
- if (c.mono)
18038
- td.dataset.mono = "";
17915
+ cellAttrs(td, c);
18039
17916
  const cell = r[c.key];
18040
17917
  if (cell) {
18041
17918
  if (cell.text.length > 40 && !c.mono && !cell.code)
18042
17919
  td.dataset.long = "";
18043
- let content;
18044
- if (cell.badge) {
18045
- content = link(doc, "knobs-wt-chip", cell.text, cell.href);
18046
- content.dataset.tone = cell.badge;
18047
- } else {
18048
- const inner = cell.code ? text(doc, "code", "", cell.text) : null;
18049
- if (cell.href) {
18050
- content = link(doc, "knobs-wt-clink", inner ? "" : cell.text, cell.href);
18051
- if (inner)
18052
- content.append(inner);
18053
- } else
18054
- content = inner ?? text(doc, "span", "", cell.text);
18055
- if (cell.tone) {
18056
- content.classList.add("knobs-wt-ctone");
18057
- content.dataset.tone = cell.tone;
18058
- }
18059
- }
18060
- td.append(content);
18061
- const go = stepButton(doc, cell.step, steps);
18062
- if (go)
18063
- td.append(go);
17920
+ cellContent(doc, td, cell, steps);
18064
17921
  }
18065
17922
  tr.append(td);
18066
17923
  }
@@ -18068,10 +17925,73 @@ function mountTable(host, m, steps) {
18068
17925
  }
18069
17926
  table.append(head, body);
18070
17927
  scroller.append(table);
18071
- host.append(scroller);
17928
+ const stack = el(doc, "div", "knobs-wt-tstack");
17929
+ stack.setAttribute("role", "list");
17930
+ if (m.caption)
17931
+ stack.setAttribute("aria-label", m.caption);
17932
+ const primary = m.columns.find((c) => c.key === m.primary) ?? m.columns[0];
17933
+ for (const r of m.rows) {
17934
+ const item = el(doc, "div", "knobs-wt-trow");
17935
+ item.setAttribute("role", "listitem");
17936
+ const title = el(doc, "div", "knobs-wt-ttitle");
17937
+ if (primary) {
17938
+ cellAttrs(title, primary);
17939
+ const cell = r[primary.key];
17940
+ if (cell)
17941
+ cellContent(doc, title, cell, steps);
17942
+ }
17943
+ const line = el(doc, "div", "knobs-wt-tline");
17944
+ line.append(title);
17945
+ const badges = el(doc, "div", "knobs-wt-tbadges");
17946
+ for (const c of m.columns) {
17947
+ const cell = r[c.key];
17948
+ if (c !== primary && cell?.badge)
17949
+ cellContent(doc, badges, cell, steps);
17950
+ }
17951
+ if (badges.childNodes.length)
17952
+ line.append(badges);
17953
+ item.append(line);
17954
+ const terms = el(doc, "dl", "knobs-wt-tterms");
17955
+ for (const c of m.columns) {
17956
+ if (c === primary)
17957
+ continue;
17958
+ const cell = r[c.key];
17959
+ if (!cell || cell.text === "" || cell.badge)
17960
+ continue;
17961
+ const dd = el(doc, "dd");
17962
+ cellAttrs(dd, c);
17963
+ dd.removeAttribute("data-align");
17964
+ cellContent(doc, dd, cell, steps);
17965
+ terms.append(text(doc, "dt", "", c.label), dd);
17966
+ }
17967
+ if (terms.childNodes.length)
17968
+ item.append(terms);
17969
+ stack.append(item);
17970
+ }
17971
+ if (m.caption)
17972
+ stack.append(text(doc, "p", "knobs-wt-tcaption", m.caption));
17973
+ box.append(scroller, stack);
17974
+ host.append(box);
17975
+ let natural = 0;
17976
+ const measureNatural = () => {
17977
+ if (!box.isConnected)
17978
+ return 0;
17979
+ const was = box.dataset.stacked;
17980
+ delete box.dataset.stacked;
17981
+ box.dataset.measure = "";
17982
+ const w = table.getBoundingClientRect().width;
17983
+ delete box.dataset.measure;
17984
+ if (was !== undefined)
17985
+ box.dataset.stacked = was;
17986
+ return w;
17987
+ };
18072
17988
  const measure = () => {
17989
+ if (!natural)
17990
+ natural = measureNatural();
17991
+ const stacked = tableStacks(m.stack, box.clientWidth, natural);
17992
+ box.toggleAttribute("data-stacked", stacked);
18073
17993
  scroller.style.setProperty("--kw-thead", `${head.offsetHeight}px`);
18074
- const scrolls = scroller.scrollWidth > scroller.clientWidth + 1 || scroller.scrollHeight > scroller.clientHeight + 1;
17994
+ const scrolls = !stacked && (scroller.scrollWidth > scroller.clientWidth + 1 || scroller.scrollHeight > scroller.clientHeight + 1);
18075
17995
  if (scrolls) {
18076
17996
  scroller.tabIndex = 0;
18077
17997
  scroller.setAttribute("role", "region");
@@ -18082,106 +18002,636 @@ function mountTable(host, m, steps) {
18082
18002
  scroller.removeAttribute("aria-label");
18083
18003
  }
18084
18004
  };
18005
+ if (m.stack === "always")
18006
+ box.dataset.stacked = "";
18085
18007
  const ro = win && "ResizeObserver" in win ? new win.ResizeObserver(measure) : null;
18086
- ro?.observe(scroller);
18008
+ ro?.observe(box);
18087
18009
  measure();
18010
+ doc.fonts?.ready.then(() => {
18011
+ if (!box.isConnected)
18012
+ return;
18013
+ natural = 0;
18014
+ measure();
18015
+ });
18088
18016
  return {
18089
18017
  dispose: () => {
18090
18018
  ro?.disconnect();
18091
- scroller.remove();
18019
+ box.remove();
18092
18020
  },
18093
18021
  context: () => ({ kind: "table" })
18094
18022
  };
18095
18023
  }
18096
- var grid2 = (v) => Math.round(v * 64) / 64;
18097
18024
  function chapterPills(host, chapters, io) {
18098
18025
  const doc = host.ownerDocument;
18099
- const win = doc.defaultView;
18100
- const bar = el(doc, "div", "knobs-wt-chapters");
18101
- bar.setAttribute("role", "group");
18102
- bar.setAttribute("aria-label", "Chapters");
18103
- const mark = el(doc, "span", "knobs-wt-chapter-mark");
18104
- mark.setAttribute("aria-hidden", "true");
18105
- bar.append(mark);
18106
- const pills = chapters.map((c) => {
18026
+ const list = el(doc, "div", "knobs-wt-chapters");
18027
+ list.setAttribute("role", "group");
18028
+ list.setAttribute("aria-label", "Chapters");
18029
+ let at = -2;
18030
+ const rows = chapters.map((c, i) => {
18107
18031
  const b = el(doc, "button", "knobs-wt-chapter");
18108
18032
  b.type = "button";
18109
- b.append(text(doc, "span", "knobs-wt-chapter-time", clock(c.start)));
18033
+ b.append(text(doc, "span", "knobs-wt-chapter-n", String(i + 1)));
18110
18034
  b.append(text(doc, "span", "knobs-wt-chapter-label", c.label));
18035
+ b.append(text(doc, "span", "knobs-wt-chapter-time", clock(c.start)));
18111
18036
  b.addEventListener("click", () => {
18112
18037
  io.onEvent?.({ what: "seek", time: Math.round(c.start * 100) / 100, detail: c.label });
18113
18038
  io.seek(c.start);
18114
- sync();
18039
+ select(i);
18115
18040
  });
18116
- bar.append(b);
18041
+ list.append(b);
18117
18042
  return b;
18118
18043
  });
18119
- host.append(bar);
18120
- const x = new Spring(0, MOTION.calm.omega, MOTION.calm.zeta, 0.25);
18121
- const y = new Spring(0, MOTION.calm.omega, MOTION.calm.zeta, 0.25);
18122
- const w = new Spring(0, MOTION.calm.omega, MOTION.calm.zeta, 0.25);
18123
- const draw = () => {
18124
- const left = grid2(x.x);
18125
- mark.style.transform = `translate(${left}px,${grid2(y.x)}px)`;
18126
- mark.style.width = `${grid2(x.x + w.x) - left}px`;
18127
- };
18128
- const loop = frameLoop(win, (dt) => {
18129
- x.step(dt);
18130
- y.step(dt);
18131
- w.step(dt);
18132
- draw();
18133
- return !x.resting || !y.resting || !w.resting;
18044
+ host.append(list);
18045
+ const pill = selectionPill(list, {
18046
+ selected: () => rows[at] ?? null,
18047
+ rows: () => rows
18134
18048
  });
18135
- let at = -2;
18136
- let shown = false;
18137
- const place = (snap) => {
18138
- const p = pills[at];
18139
- if (!p) {
18140
- mark.removeAttribute("data-on");
18141
- pills.forEach((b) => b.removeAttribute("aria-current"));
18142
- shown = false;
18049
+ const select = (i) => {
18050
+ if (i === at)
18143
18051
  return;
18144
- }
18145
- pills.forEach((b, i) => i === at ? b.setAttribute("aria-current", "true") : b.removeAttribute("aria-current"));
18146
- const to = [p.offsetLeft, p.offsetTop, p.offsetWidth];
18147
- if (snap || !shown || reducedMotion(win)) {
18148
- x.snap(to[0]);
18149
- y.snap(to[1]);
18150
- w.snap(to[2]);
18151
- draw();
18152
- } else {
18153
- x.set(to[0]);
18154
- y.set(to[1]);
18155
- w.set(to[2]);
18156
- loop.wake();
18157
- }
18158
- mark.style.height = `${p.offsetHeight}px`;
18159
- mark.dataset.on = "";
18160
- shown = true;
18161
- };
18162
- const sync = () => {
18163
- const next = chapterAt(chapters, io.now());
18164
- if (next === at)
18165
- return;
18166
- at = next;
18167
- place(false);
18052
+ at = i;
18053
+ rows.forEach((b, k) => k === at ? b.setAttribute("aria-current", "true") : b.removeAttribute("aria-current"));
18054
+ pill.update();
18168
18055
  };
18056
+ const sync = () => select(chapterAt(chapters, io.now()));
18169
18057
  sync();
18170
- const ro = "ResizeObserver" in win ? new win.ResizeObserver(() => place(true)) : null;
18171
- ro?.observe(bar);
18172
18058
  return {
18173
18059
  sync,
18174
18060
  get current() {
18175
18061
  return chapters[at] ?? null;
18176
18062
  },
18063
+ dispose() {
18064
+ pill.dispose();
18065
+ list.remove();
18066
+ }
18067
+ };
18068
+ }
18069
+ function disclosure(host, options) {
18070
+ const doc = host.ownerDocument;
18071
+ const win = doc.defaultView;
18072
+ let open = options.open === true;
18073
+ const root = el(doc, "div", "knobs-wt-disc");
18074
+ const toggle = el(doc, "button", "knobs-wt-disc-toggle");
18075
+ toggle.type = "button";
18076
+ toggle.innerHTML = GLYPH.caret;
18077
+ toggle.append(text(doc, "span", "", options.label));
18078
+ if (options.count !== undefined)
18079
+ toggle.append(text(doc, "span", "knobs-wt-disc-count", String(options.count)));
18080
+ const viewport = el(doc, "div", "knobs-wt-disc-body");
18081
+ const content = el(doc, "div", "knobs-wt-disc-content");
18082
+ content.append(options.content);
18083
+ viewport.append(content);
18084
+ root.append(toggle, viewport);
18085
+ host.append(root);
18086
+ const STATE = springMotion(SPRINGS.default);
18087
+ const h = new Spring(0, STATE.omega, STATE.zeta, 0.25);
18088
+ let full = 0;
18089
+ const paint = (moving) => {
18090
+ const k = full > 0 ? Math.max(0, Math.min(1, h.x / full)) : open ? 1 : 0;
18091
+ viewport.style.height = !moving && open ? "" : `${Math.max(0, h.x).toFixed(2)}px`;
18092
+ viewport.toggleAttribute("data-moving", moving);
18093
+ content.style.opacity = (open ? Math.min(1, k * 1.25) : Math.max(0, k * 1.5 - 0.5)).toFixed(3);
18094
+ };
18095
+ const loop = frameLoop(win, (dt) => {
18096
+ h.step(dt);
18097
+ const moving = !h.resting;
18098
+ paint(moving);
18099
+ return moving;
18100
+ });
18101
+ const sync = () => {
18102
+ root.toggleAttribute("data-open", open);
18103
+ toggle.setAttribute("aria-expanded", String(open));
18104
+ viewport.inert = !open;
18105
+ };
18106
+ sync();
18107
+ h.snap(0);
18108
+ paint(false);
18109
+ if (open) {
18110
+ viewport.style.height = "";
18111
+ content.style.opacity = "1";
18112
+ }
18113
+ const api = {
18114
+ element: root,
18115
+ get open() {
18116
+ return open;
18117
+ },
18118
+ toggle(next = !open) {
18119
+ if (next === open)
18120
+ return;
18121
+ open = next;
18122
+ sync();
18123
+ full = content.offsetHeight;
18124
+ if (reducedMotion(win)) {
18125
+ loop.stop();
18126
+ h.snap(open ? full : 0);
18127
+ viewport.style.height = open ? "" : "0px";
18128
+ content.style.opacity = open ? "1" : "0";
18129
+ return;
18130
+ }
18131
+ if (h.resting)
18132
+ h.snap(open ? 0 : full);
18133
+ h.set(open ? full : 0);
18134
+ paint(true);
18135
+ loop.wake();
18136
+ },
18177
18137
  dispose() {
18178
18138
  loop.stop();
18179
- ro?.disconnect();
18180
- bar.remove();
18139
+ root.remove();
18181
18140
  }
18182
18141
  };
18142
+ toggle.addEventListener("click", () => api.toggle());
18143
+ return api;
18144
+ }
18145
+
18146
+ // src/ui/walkthrough-media.ts
18147
+ init_compare_model();
18148
+ init_glide();
18149
+ init_icons();
18150
+ init_look();
18151
+
18152
+ // src/ui/walkthrough-css.ts
18153
+ init_motion();
18154
+ var snappy3 = springCss(SPRINGS.snappy);
18155
+ var calm = springCss(SPRINGS.calm);
18156
+ var state = springCss(SPRINGS.default);
18157
+ var quick = springCss(FADE);
18158
+ var fade2 = "-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent)";
18159
+ var walkthroughCss = `
18160
+ :where(.knobs-walkthrough,.knobs-walkthrough *,.knobs-wt-slot,.knobs-wt-slot *){box-sizing:border-box}
18161
+ .knobs-ui.knobs-walkthrough{--kw-snappy:${snappy3.easing};--kw-snappy-ms:${snappy3.duration}ms;--kw-calm:${calm.easing};--kw-calm-ms:${calm.duration}ms;--kw-state:${state.easing};--kw-state-ms:${state.duration}ms;--kw-quick:${quick.easing};--kw-quick-ms:${quick.duration}ms;
18162
+ --kw-measure:var(--k-walkthrough-measure,68ch);--kw-width:var(--k-walkthrough-width,680px);--kw-rail-w:220px;--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info));
18163
+ --kw-card:var(--_ku-pane-bg);--kw-card-ring:var(--_ku-pane-ring);--kw-under:var(--_k-bg-page);--kw-ink:var(--k-walkthrough-ink,#ff375f);--kw-strong:var(--_k-weight-label);
18164
+ container:knobs-wt / inline-size;display:block;width:100%;color:var(--_k-fg);font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;text-align:left;
18165
+ letter-spacing:normal;-webkit-font-smoothing:antialiased}
18166
+ .knobs-walkthrough [hidden]{display:none!important}
18167
+ /* (0,0,1): a host's element rules stop here, and every class rule below wins over it. */
18168
+ :where(.knobs-walkthrough) :is(h1,h2,h3,p,ol,ul,dl,dd){margin:0;font:inherit;letter-spacing:inherit}
18169
+ .knobs-walkthrough a{color:inherit;text-decoration:underline;text-decoration-color:color-mix(in srgb,currentColor 35%,transparent);text-underline-offset:2px;transition:text-decoration-color .15s}
18170
+ .knobs-walkthrough a:hover{text-decoration-color:currentColor}
18171
+ .knobs-walkthrough code,.knobs-wt-slot code{font:var(--_k-type-label) var(--_k-mono);padding:1px 5px;border-radius:calc(var(--_k-radius) / 2);background:var(--_k-fill)}
18172
+ :where(.knobs-ui) :is(.knobs-wt-btn,.knobs-wt-pick,.knobs-wt-toc-row,button.knobs-wt-stitle,.knobs-wt-chapter,.knobs-wt-disc-toggle){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;
18173
+ background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
18174
+
18175
+ /* ——— layout: a readable column, centred, with a slim contents rail beside it (right by default);
18176
+ one column where it's narrow ——— */
18177
+ .knobs-wt-layout{display:grid;grid-template-columns:minmax(0,var(--kw-width)) var(--kw-rail-w);justify-content:center;gap:var(--_k-space-column);align-items:start}
18178
+ .knobs-walkthrough[data-rail="right"] .knobs-wt-main{grid-column:1;grid-row:1}
18179
+ .knobs-walkthrough[data-rail="right"] .knobs-wt-rail{grid-column:2;grid-row:1}
18180
+ .knobs-walkthrough[data-rail="left"] .knobs-wt-layout{grid-template-columns:var(--kw-rail-w) minmax(0,var(--kw-width))}
18181
+ .knobs-walkthrough[data-rail="none"] .knobs-wt-layout{grid-template-columns:minmax(0,var(--kw-width))}
18182
+ .knobs-wt-main{display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0;padding-bottom:12px}
18183
+ /* The intro, the updates and each section: a section's room apart, the hairline in the middle. */
18184
+ .knobs-wt-flow{display:flex;flex-direction:column;gap:calc(var(--_k-space-section) / 2);min-width:0}
18185
+ .knobs-wt-flow>*+*{padding-top:calc(var(--_k-space-section) / 2);box-shadow:inset 0 1px 0 var(--_k-border-subtle)}
18186
+ @container knobs-wt (max-width:940px){.knobs-walkthrough .knobs-wt-layout{grid-template-columns:minmax(0,1fr);gap:var(--_k-space-title)}
18187
+ .knobs-walkthrough[data-rail] .knobs-wt-layout>*{grid-column:auto;grid-row:auto}}
18188
+
18189
+ /* ——— one line of facts: small, faint, joined by ·, its trailing edge fading ——— */
18190
+ .knobs-wt-metaline{display:flex;align-items:baseline;gap:var(--_k-space-inside);min-width:0;overflow:hidden;white-space:nowrap;font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3);${fade2}}
18191
+ .knobs-wt-metaline>*{flex:none}
18192
+ .knobs-wt-metaline>*+*::before{content:"·";margin-right:var(--_k-space-inside);color:var(--_k-fg-3)}
18193
+ .knobs-walkthrough .knobs-wt-metaline a{display:inline-flex;align-items:baseline;color:inherit;text-decoration:none;transition:color calc(var(--_k-hover-out) * 1ms)}
18194
+ .knobs-walkthrough .knobs-wt-metaline a:hover{color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18195
+ .knobs-wt-metaline svg{align-self:center;width:12px;height:12px;margin-left:2px}
18196
+ .knobs-wt-kicker-lead{color:var(--_k-fg);font-weight:var(--kw-strong)}
18197
+ .knobs-wt-metaline [data-status="on"]{color:var(--_k-fg)}
18198
+ .knobs-wt-metaline [data-status="warn"]{color:var(--_k-warn)}
18199
+
18200
+ /* ——— the page's head ——— */
18201
+ .knobs-wt-head{display:flex;flex-direction:column;gap:var(--_k-space-title)}
18202
+ .knobs-wt-title{font-size:var(--_k-type-title)!important;line-height:var(--_k-leading-title);font-weight:var(--kw-strong)!important;text-wrap:balance}
18203
+ .knobs-wt-summary{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
18204
+ .knobs-wt-meta{display:flex;flex-wrap:wrap;gap:var(--_k-space-inside) var(--_k-space-title);color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
18205
+ .knobs-wt-meta>span{display:inline-flex;gap:var(--_k-space-inside);white-space:nowrap}
18206
+ .knobs-wt-meta b{font-weight:var(--_k-weight);color:var(--_k-fg-2);font-family:var(--_k-mono)}
18207
+
18208
+ /* ——— a section: a PR or topic. Its kicker leads the meta line; its title outranks the page's ——— */
18209
+ .knobs-wt-section{display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0}
18210
+ .knobs-wt-sintro{display:flex;flex-direction:column;gap:var(--_k-space-title);min-width:0}
18211
+ .knobs-wt-shead{display:flex;flex-direction:column;gap:var(--_k-space-inside);min-width:0;scroll-margin-top:16px}
18212
+ .knobs-wt-heading{font-size:var(--_k-type-heading)!important;line-height:var(--_k-leading-heading);font-weight:var(--kw-strong)!important;letter-spacing:-.01em!important;text-wrap:balance}
18213
+ .knobs-wt-ssum{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
18214
+ .knobs-wt-chip{display:inline-flex;align-items:center;gap:6px;height:20px;padding:0 8px;border-radius:var(--_k-round);font-size:var(--_k-type-small);font-weight:var(--kw-strong);white-space:nowrap;
18215
+ --kw-tone:var(--_k-fg-2);color:var(--kw-tone);background:color-mix(in srgb,var(--kw-tone) 14%,transparent)}
18216
+ .knobs-wt-chip::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
18217
+ :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="good"]{--kw-tone:var(--kw-good)}
18218
+ :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="warn"]{--kw-tone:var(--_k-warn)}
18219
+ :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="bad"]{--kw-tone:var(--_k-danger)}
18220
+ :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="info"]{--kw-tone:var(--kw-info)}
18221
+ :is(.knobs-wt-chip,.knobs-wt-toc-glyph)[data-tone="dim"]{--kw-tone:var(--_k-fg-2)}
18222
+
18223
+ /* ——— a step: on the page by default, a group's room between steps with a hairline in the middle;
18224
+ or (cards) one card each, the one surface its pieces sit on ——— */
18225
+ .knobs-walkthrough:not([data-cards]) .knobs-wt-step:not([data-full]){padding:0;border-radius:0;background:transparent;box-shadow:none}
18226
+ .knobs-walkthrough:not([data-cards]) .knobs-wt-step+.knobs-wt-step::before{content:"";position:absolute;left:0;right:0;top:calc(var(--_k-space-group) / -2);height:1px;background:var(--_k-border-subtle);pointer-events:none}
18227
+ .knobs-wt-step{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-title);padding:var(--_k-pad-card);border-radius:var(--_k-radius-panel);background:var(--kw-card);box-shadow:var(--kw-card-ring);
18228
+ scroll-margin-top:16px;outline:none;container:knobs-wt-step / inline-size}
18229
+ .knobs-walkthrough[data-cards]{--kw-under:var(--kw-card)}
18230
+ .knobs-walkthrough[data-cards][data-frame="flat"] .knobs-wt-step{box-shadow:inset 0 0 0 1px var(--_k-border)}
18231
+ .knobs-wt-step-head{display:flex;flex-direction:column;gap:var(--_k-space-inside);min-width:0}
18232
+ .knobs-wt-stepTitle{min-width:0;font-weight:var(--kw-strong)!important;text-wrap:balance}
18233
+ .knobs-wt-caption{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
18234
+ .knobs-wt-question{font-weight:var(--kw-strong)!important;max-width:var(--kw-measure)}
18235
+ .knobs-wt-media{display:grid;grid-template-columns:repeat(var(--kw-n,1),minmax(0,1fr));gap:var(--_k-space-group) var(--_k-space-column);min-width:0}
18236
+ @container knobs-wt-step (max-width:640px){.knobs-wt-media{grid-template-columns:minmax(0,1fr)}}
18237
+
18238
+ /* ——— the feed: updates and answers, newest first. Tight inside an item (4 / 8 / 4), a group's
18239
+ room between items with the hairline in the middle; no boxes ——— */
18240
+ .knobs-wt-feed{display:flex;flex-direction:column;gap:var(--_k-space-title);min-width:0;scroll-margin-top:16px}
18241
+ .knobs-wt-feed-head{display:flex;align-items:baseline;gap:var(--_k-space-sibling)}
18242
+ .knobs-wt-feed-title{font-weight:var(--kw-strong)!important}
18243
+ .knobs-wt-feed-count{font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}
18244
+ .knobs-wt-feed-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0}
18245
+ .knobs-wt-fi{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-sibling);min-width:0}
18246
+ .knobs-wt-fi+.knobs-wt-fi::before{content:"";position:absolute;left:0;right:0;top:calc(var(--_k-space-group) / -2);height:1px;background:var(--_k-border-subtle)}
18247
+ .knobs-wt-fi-q,.knobs-wt-fi-a{display:flex;flex-direction:column;gap:var(--_k-space-inside);min-width:0}
18248
+ .knobs-wt-fi-ask{max-width:var(--kw-measure);color:var(--_k-fg);font-weight:var(--kw-strong);text-wrap:pretty}
18249
+ .knobs-wt-fi-text{max-width:var(--kw-measure);color:var(--_k-fg);text-wrap:pretty}
18250
+ .knobs-wt-fi-answer{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
18251
+ :where(.knobs-ui) .knobs-wt-fi-step{all:revert;box-sizing:border-box;font:inherit;color:inherit;background:transparent;border:0;margin:0;padding:0;cursor:pointer;outline:none;
18252
+ transition:color calc(var(--_k-hover-out) * 1ms)}
18253
+ .knobs-ui .knobs-wt-fi-step:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18254
+ :where(.knobs-ui) .knobs-wt-steplink{all:revert;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:0 0 0 2px;padding:0;vertical-align:-5px;border:0;
18255
+ border-radius:calc(var(--_k-radius) / 2);background:transparent;color:var(--_k-fg-3);cursor:pointer;outline:none;transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}
18256
+ .knobs-ui .knobs-wt-steplink:is(:hover,:focus-visible){background:var(--_k-fill);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18257
+ .knobs-wt-steplink svg{width:12px;height:12px}
18258
+
18259
+ /* ——— a disclosure: a small faint toggle with a caret, no fill. The body's height springs (in
18260
+ script) and its content fades in place, top-anchored, under a soft bottom mask ——— */
18261
+ .knobs-wt-disc{display:flex;flex-direction:column;min-width:0}
18262
+ .knobs-ui .knobs-wt-disc-toggle{display:inline-flex;align-items:center;gap:var(--_k-space-inside);width:max-content;height:20px;color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);cursor:pointer;
18263
+ border-radius:calc(var(--_k-radius) / 2);transition:color calc(var(--_k-hover-out) * 1ms)}
18264
+ .knobs-ui .knobs-wt-disc-toggle:is(:hover,:focus-visible),.knobs-wt-disc[data-open]>.knobs-wt-disc-toggle{color:var(--_k-fg-2);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18265
+ .knobs-ui .knobs-wt-disc-toggle:focus-visible{box-shadow:0 0 0 2px var(--_k-focus)}
18266
+ .knobs-wt-disc-toggle svg{flex:none;width:12px;height:12px;transition:rotate var(--kw-state-ms) var(--kw-state)}
18267
+ .knobs-wt-disc[data-open]>.knobs-wt-disc-toggle svg{rotate:90deg}
18268
+ .knobs-wt-disc-count{color:var(--_k-fg-3)}
18269
+ .knobs-wt-disc-body{overflow:hidden}
18270
+ .knobs-wt-disc[data-open]>.knobs-wt-disc-body:not([data-moving]){overflow:visible}
18271
+ .knobs-wt-disc-body[data-moving]{-webkit-mask-image:linear-gradient(#000 calc(100% - 16px),transparent);mask-image:linear-gradient(#000 calc(100% - 16px),transparent)}
18272
+ .knobs-wt-disc-content{padding-top:var(--_k-space-inside)}
18273
+ /* A trail sits on the toggle's label edge: faint lines, the time trailing. */
18274
+ .knobs-wt-trail{display:flex;flex-direction:column;list-style:none;padding:0 0 0 calc(12px + var(--_k-space-inside));font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3);min-width:0}
18275
+ .knobs-wt-trail li{display:flex;align-items:baseline;gap:var(--_k-space-sibling);min-width:0;white-space:nowrap}
18276
+ .knobs-wt-trail-text{flex:0 1 auto;min-width:0;overflow:hidden;${fade2}}
18277
+ .knobs-wt-trail-at{flex:none}
18278
+
18279
+ /* ——— a step's sent notes ——— */
18280
+ .knobs-wt-notes{display:flex;flex-direction:column}
18281
+ .knobs-wt-notes:empty{display:none}
18282
+ .knobs-wt-n{display:flex;align-items:flex-start;gap:var(--_k-space-sibling);padding:6px 0;overflow:hidden;
18283
+ transition:height var(--kw-snappy-ms) var(--kw-snappy),opacity var(--kw-snappy-ms) var(--kw-snappy),filter var(--kw-snappy-ms) var(--kw-snappy)}
18284
+ .knobs-wt-n[data-entering]{opacity:0;filter:blur(3px)}
18285
+ .knobs-wt-n[data-leaving]{opacity:0;filter:blur(3px);padding-block:0}
18286
+ .knobs-wt-n+.knobs-wt-n{box-shadow:inset 0 1px 0 var(--_k-border-subtle)}
18287
+ .knobs-wt-n-meta{flex:none;min-width:44px;color:var(--_k-fg-3);font:var(--_k-weight) var(--_k-type-small)/var(--_k-leading-body) var(--_k-mono)}
18288
+ .knobs-wt-n-meta[data-seek]{cursor:pointer}
18289
+ .knobs-wt-n-meta[data-seek]:hover{color:var(--_k-fg)}
18290
+ .knobs-wt-n-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
18291
+ .knobs-wt-n-voice{height:30px;max-width:320px}
18292
+ .knobs-wt-n-ink{display:block;width:120px;max-height:80px;border-radius:calc(var(--_k-radius) / 2);background:var(--_k-fill)}
18293
+ .knobs-wt-n-ink path{fill:none;stroke:var(--kw-ink);stroke-width:2px;vector-effect:non-scaling-stroke;stroke-linecap:round;stroke-linejoin:round}
18294
+
18295
+ /* ——— the note bar, docked at the bottom of the column. It holds its own place at the page's end
18296
+ (so the last step stays clear of it), and script keeps its height as the scroller's
18297
+ scroll-padding-bottom ——— */
18298
+ .knobs-wt-dock{position:sticky;bottom:12px;z-index:40;display:grid;pointer-events:none}
18299
+ .knobs-wt-dock>*{pointer-events:auto}
18300
+ .knobs-wt-dock>:is(.knobs-wt-notebar,.knobs-wt-launch){grid-area:1/1}
18301
+ .knobs-wt-dock[data-mode="docked"]>.knobs-wt-launch{display:none}
18302
+ /* Collapsed: the launcher and the bar swap in place, a quick fade with a little scale and blur
18303
+ from the launcher's corner; the exit is the shorter. */
18304
+ :where(.knobs-ui) .knobs-wt-launch{all:revert;box-sizing:border-box;justify-self:end;align-self:end;display:inline-flex;align-items:center;gap:6px;height:32px;margin:0;padding:0 10px 0 9px;border:0;border-radius:var(--_k-round);
18305
+ background:var(--_k-bg);color:var(--_k-fg-2);box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);font:var(--kw-strong) var(--_k-type-small)/1 var(--_k-font);cursor:pointer;outline:none;transform-origin:100% 100%;
18306
+ transition:color calc(var(--_k-hover-out) * 1ms),opacity var(--kw-quick-ms) var(--kw-quick),scale var(--kw-quick-ms) var(--kw-quick),filter var(--kw-quick-ms) var(--kw-quick)}
18307
+ .knobs-ui .knobs-wt-launch:is(:hover,:focus-visible){color:var(--_k-fg)}
18308
+ .knobs-ui .knobs-wt-launch:focus-visible{box-shadow:var(--_k-focus-ring)}
18309
+ .knobs-wt-launch svg{width:16px;height:16px}
18310
+ .knobs-wt-launch kbd{font:inherit;font-weight:var(--_k-weight);color:var(--_k-fg-3)}
18311
+ .knobs-wt-dock[data-mode="collapsed"] .knobs-wt-notebar{transform-origin:100% 100%;transition:opacity var(--kw-quick-ms) var(--kw-quick),scale var(--kw-quick-ms) var(--kw-quick),filter var(--kw-quick-ms) var(--kw-quick)}
18312
+ .knobs-wt-dock[data-mode="collapsed"]:not([data-open])>.knobs-wt-notebar,.knobs-wt-dock[data-mode="collapsed"][data-open]>.knobs-wt-launch{opacity:0;scale:.98;filter:blur(3px);pointer-events:none;
18313
+ transition-duration:calc(var(--kw-quick-ms) * .8)}
18314
+ .knobs-wt-notebar{display:flex;flex-direction:column;gap:2px;padding:6px 6px 6px 14px;border-radius:calc(var(--_k-radius-panel) + 2px);background:var(--_k-bg);
18315
+ box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay)}
18316
+ .knobs-wt-on{padding-top:3px;color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);white-space:nowrap;overflow:hidden;${fade2}}
18317
+ .knobs-wt-row{display:flex;align-items:flex-end;gap:4px}
18318
+ :where(.knobs-ui) :is(.knobs-wt-input,.knobs-wt-tool,.knobs-wt-x,.knobs-wt-back){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;
18319
+ border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
18320
+ .knobs-ui .knobs-wt-input{flex:1;min-width:0;height:32px;max-height:140px;padding:6px 0;resize:none;color:var(--_k-fg);caret-color:var(--_k-accent);line-height:var(--_k-leading-body);font-size:var(--_k-type-body)}
18321
+ .knobs-ui .knobs-wt-input::placeholder{color:var(--_k-fg-3)}
18322
+ .knobs-wt-attach{display:flex;gap:4px;align-items:center;align-self:center}
18323
+ .knobs-wt-attach:empty{display:none}
18324
+ .knobs-wt-chip-a{display:inline-flex;align-items:center;gap:2px;height:24px;padding:0 2px 0 9px;border-radius:var(--_k-round);background:var(--_k-fill-selected);font-size:var(--_k-type-small);white-space:nowrap}
18325
+ .knobs-ui .knobs-wt-x{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;color:var(--_k-fg-3);cursor:pointer;transition:background .15s,color .15s}
18326
+ .knobs-ui .knobs-wt-x:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
18327
+ .knobs-wt-x svg{width:12px;height:12px}
18328
+ .knobs-ui .knobs-wt-tool{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;min-width:32px;height:32px;padding:0 8px;border-radius:calc(var(--_k-radius) + 2px);
18329
+ color:var(--_k-fg-2);cursor:pointer;transition:background .15s,color .15s}
18330
+ .knobs-ui .knobs-wt-tool:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
18331
+ .knobs-ui .knobs-wt-tool[aria-pressed="true"]{background:var(--_k-fill-selected);color:var(--_k-fg)}
18332
+ .knobs-wt-tool svg{width:16px;height:16px}
18333
+ .knobs-wt-rec{font-size:var(--_k-type-small)}
18334
+ .knobs-wt-rec:empty{display:none}
18335
+ .knobs-ui .knobs-wt-tool[data-send]{color:var(--_k-fg-3)}
18336
+ .knobs-ui .knobs-wt-tool[data-send][data-ready]{background:var(--_k-accent);color:var(--_k-bg)}
18337
+ /* Back: in place over the bar for a few seconds after a note moves the page on. */
18338
+ .knobs-ui .knobs-wt-back{position:absolute;left:8px;bottom:calc(100% + 8px);display:inline-flex;align-items:center;gap:var(--_k-space-inside);max-width:min(360px,calc(100% - 16px));height:28px;padding:0 12px 0 9px;
18339
+ border-radius:var(--_k-round);background:var(--_k-bg);color:var(--_k-fg);box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);font-size:var(--_k-type-small);font-weight:var(--kw-strong);cursor:pointer;
18340
+ opacity:0;scale:.96;filter:blur(3px);pointer-events:none;transition:opacity .11s cubic-bezier(.4,0,1,1),scale .11s cubic-bezier(.4,0,1,1),filter .11s cubic-bezier(.4,0,1,1)}
18341
+ .knobs-ui .knobs-wt-back[data-on]{opacity:1;scale:1;filter:none;pointer-events:auto;transition:opacity .15s cubic-bezier(.2,.8,.2,1),scale .15s cubic-bezier(.2,.8,.2,1),filter .15s cubic-bezier(.2,.8,.2,1)}
18342
+ .knobs-ui .knobs-wt-back:hover{background:var(--_k-fill-selected)}
18343
+ .knobs-wt-back svg{flex:none;width:12px;height:12px}
18344
+ .knobs-wt-back-label{min-width:0;overflow:hidden;white-space:nowrap;${fade2}}
18345
+
18346
+ /* ——— ink over a step's media ——— */
18347
+ .knobs-wt-media,.knobs-wt-options{position:relative}
18348
+ .knobs-wt-ink{position:absolute;inset:0;z-index:5;width:100%;height:100%;overflow:visible;pointer-events:none;touch-action:none}
18349
+ .knobs-wt-ink[data-on]{pointer-events:auto;cursor:crosshair}
18350
+ .knobs-wt-ink path{fill:none;stroke:var(--kw-ink);stroke-width:3px;vector-effect:non-scaling-stroke;stroke-linecap:round;stroke-linejoin:round}
18351
+
18352
+ /* ——— full-window: the step over the page above the note bar, its media filling it; its place held
18353
+ in the page. The one raised surface ——— */
18354
+ .knobs-wt-scrim{position:fixed;inset:0;z-index:30;background:color-mix(in srgb,var(--_k-bg-page) 82%,transparent);opacity:0;pointer-events:none}
18355
+ .knobs-wt-scrim[data-on]{pointer-events:auto}
18356
+ .knobs-wt-hold-card{flex:none}
18357
+ .knobs-wt-step[data-full]{position:fixed;z-index:35;margin:0;overflow:hidden;overscroll-behavior:contain;padding:var(--_k-pad-card);border-radius:var(--_k-radius-panel);box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);background:var(--_k-bg)}
18358
+ .knobs-wt-step[data-full]::before{content:none}
18359
+ .knobs-wt-step[data-full]>:not(.knobs-wt-media){flex:none}
18360
+ .knobs-wt-step[data-full] .knobs-wt-notes{display:none}
18361
+ .knobs-wt-step[data-full]>.knobs-wt-media{flex:1;min-height:0;grid-auto-rows:minmax(0,1fr)}
18362
+ .knobs-wt-step[data-full] .knobs-wt-slot{display:flex;flex-direction:column;min-height:0}
18363
+ .knobs-wt-step[data-full] .knobs-wt-body{flex:1;min-height:0}
18364
+ .knobs-wt-step[data-full] .knobs-wt-fit{height:100%;transition:none}
18365
+ .knobs-wt-step[data-full] .knobs-wt-frame{height:100%;aspect-ratio:auto}
18366
+
18367
+ /* ——— a taste test: the options side by side, Kit's pick checked and the rest stepping back. On
18368
+ the step's card, so no fill of their own ——— */
18369
+ .knobs-wt-options{display:grid;grid-template-columns:repeat(var(--kw-n,2),minmax(0,1fr));gap:var(--_k-space-group) var(--_k-space-column)}
18370
+ @container knobs-wt-step (max-width:640px){.knobs-wt-options{grid-template-columns:minmax(0,1fr)}}
18371
+ .knobs-wt-option{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-sibling);min-width:0}
18372
+ .knobs-wt-option>:not(.knobs-wt-pick){transition:opacity var(--kw-snappy-ms) var(--kw-snappy)}
18373
+ .knobs-wt-options[data-has-pick] .knobs-wt-option:not([data-picked])>:not(.knobs-wt-pick){opacity:.55}
18374
+ .knobs-ui .knobs-wt-pick{display:flex;align-items:center;gap:8px;width:100%;min-height:28px;margin:0 -6px;width:calc(100% + 12px);padding:0 6px;border-radius:var(--_k-radius);cursor:pointer;color:var(--_k-fg-2);text-align:left;
18375
+ transition:background calc(var(--_k-hover-out) * 1ms)}
18376
+ .knobs-ui .knobs-wt-pick:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_k-fill);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18377
+ .knobs-wt-letter{flex:none;font-weight:var(--kw-strong);color:var(--_k-fg)}
18378
+ .knobs-wt-olabel{flex:1;min-width:0;font-weight:var(--kw-strong);color:var(--_k-fg)}
18379
+ .knobs-wt-suggested{flex:none;font-size:var(--_k-type-small);color:var(--_k-fg-3)}
18380
+ .knobs-wt-check{flex:none;display:flex;width:18px;height:18px;align-items:center;justify-content:center;border-radius:50%;background:var(--_k-fill);color:transparent;
18381
+ transition:background var(--kw-snappy-ms) var(--kw-snappy),color var(--kw-snappy-ms) var(--kw-snappy)}
18382
+ .knobs-wt-check svg{width:12px;height:12px}
18383
+ .knobs-wt-option[data-picked] .knobs-wt-check{background:var(--_k-accent);color:var(--_k-bg)}
18384
+ .knobs-wt-key{display:inline-flex;align-items:center;justify-content:center;min-width:15px;height:15px;padding:0 3px;border-radius:4px;background:var(--_k-fill);color:var(--_k-fg-3);
18385
+ font:var(--kw-strong) var(--_k-type-small)/1 var(--_k-font);opacity:.5;transition:opacity .15s}
18386
+ .knobs-wt-step[data-current] .knobs-wt-key{opacity:1}
18387
+ .knobs-wt-ocap{color:var(--_k-fg-2);max-width:var(--kw-measure)}
18388
+
18389
+ /* ——— the rail: progress, contents, export ——— */
18390
+ .knobs-wt-rail{position:sticky;top:16px;display:flex;flex-direction:column;gap:var(--_k-space-title);max-height:calc(100vh - 32px);min-height:0;font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
18391
+ .knobs-wt-progress{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
18392
+ .knobs-wt-count{display:flex;align-items:baseline;justify-content:space-between;color:var(--_k-fg-2)}
18393
+ .knobs-wt-count b{color:var(--_k-fg);font-weight:var(--kw-strong)}
18394
+ .knobs-wt-bar{position:relative;height:4px;border-radius:2px;overflow:hidden;background:var(--_k-fill)}
18395
+ .knobs-wt-fill{position:absolute;inset:0;border-radius:inherit;background:var(--_k-accent);transform-origin:0 50%;transform:scaleX(var(--kw-done,0))}
18396
+ .knobs-walkthrough[data-settled] .knobs-wt-fill{transition:transform var(--kw-calm-ms) var(--kw-calm)}
18397
+ /* The contents: rows centred on one spine, title rows (32 high, the control radius) and the
18398
+ steps under them (20 high, half of it), bled out so their text sits on the rail's edge; the
18399
+ selection pill slides to the current step, and base rows never brighten for being current. */
18400
+ .knobs-wt-toc{position:relative;flex:1 1 auto;min-height:0;overflow:auto;scrollbar-width:none;display:flex;flex-direction:column;gap:var(--_k-space-sibling);margin:0 calc(var(--_k-space-sibling) * -1);padding:0 0 8px}
18401
+ .knobs-wt-toc::-webkit-scrollbar{display:none}
18402
+ .knobs-wt-toc-group{display:flex;flex-direction:column;min-width:0}
18403
+ .knobs-ui .knobs-wt-toc-row{position:relative;display:grid;grid-template-columns:var(--kw-toc-n,0px) minmax(0,1fr) auto;column-gap:var(--_k-space-sibling);align-items:center;flex:none;width:100%;height:32px;
18404
+ padding:0 var(--_k-space-sibling);border-radius:var(--_k-radius);color:var(--_k-fg-2);font-size:var(--_k-type-body);line-height:var(--_k-leading-body);font-weight:var(--kw-strong);text-align:left;white-space:nowrap;cursor:pointer;
18405
+ transition:color calc(var(--_k-hover-out) * 1ms)}
18406
+ .knobs-ui .knobs-wt-toc-sub{height:20px;border-radius:calc(var(--_k-radius) / 2);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);font-weight:var(--_k-weight);color:var(--_k-fg-3)}
18407
+ .knobs-wt-toc[data-bare] .knobs-wt-toc-row{grid-template-columns:minmax(0,1fr) auto}
18408
+ .knobs-wt-toc[data-bare] .knobs-wt-toc-n{display:none}
18409
+ .knobs-ui .knobs-wt-toc-row:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18410
+ .knobs-ui .knobs-wt-toc-sub:is(:hover,:focus-visible){color:var(--_k-fg-2)}
18411
+ .knobs-ui .knobs-wt-toc-row:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
18412
+ .knobs-wt-toc-n,.knobs-wt-toc-end{display:flex;align-items:center;font-size:var(--_k-type-small);line-height:var(--_k-leading-small);font-weight:var(--_k-weight);color:var(--_k-fg-3)}
18413
+ .knobs-wt-toc-row[data-active] .knobs-wt-toc-n{color:var(--_k-fg-2)}
18414
+ .knobs-wt-toc-label{min-width:0;overflow:hidden;${fade2}}
18415
+ .knobs-pick-copy .knobs-wt-toc-n,.knobs-pick-copy .knobs-wt-toc-end{color:var(--_k-fg-2)}
18416
+ /* A step's state: an open ring, a filled dot once answered, nothing to answer a faint ring. */
18417
+ .knobs-wt-toc-glyph{--kw-tone:var(--_k-fg-3);display:block;width:6px;height:6px;border-radius:50%;box-shadow:inset 0 0 0 1.5px var(--kw-tone);
18418
+ transition:background var(--kw-snappy-ms) var(--kw-snappy),box-shadow var(--kw-snappy-ms) var(--kw-snappy)}
18419
+ .knobs-wt-toc-glyph[data-tone="accent"]{--kw-tone:var(--_k-accent)}
18420
+ .knobs-wt-toc-glyph[data-done]{background:var(--kw-tone)}
18421
+ .knobs-wt-toc-glyph[data-tone="none"]{box-shadow:inset 0 0 0 1px var(--_k-border)}
18422
+ .knobs-wt-actions{display:flex;flex-wrap:wrap;gap:4px}
18423
+ .knobs-ui .knobs-wt-btn{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 9px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:pointer;white-space:nowrap;
18424
+ transition:background .15s,color .15s}
18425
+ .knobs-ui .knobs-wt-btn:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
18426
+ .knobs-ui .knobs-wt-btn[data-primary]{background:var(--_k-fill-selected);color:var(--_k-fg)}
18427
+ .knobs-ui .knobs-wt-btn[data-primary]:is(:hover,:focus-visible){background:color-mix(in srgb,var(--_k-fg) 16%,transparent)}
18428
+ .knobs-wt-keys{color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
18429
+ .knobs-wt-keys kbd{font:inherit;color:var(--_k-fg-2)}
18430
+ /* Narrow: the rail becomes a bar along the top, with the progress and the export only. */
18431
+ @container knobs-wt (max-width:940px){
18432
+ .knobs-wt-rail{z-index:4;flex-direction:row;align-items:center;gap:var(--_k-space-title);max-height:none;margin:0 calc(var(--_k-pad-page) * -1);padding:8px var(--_k-pad-page);top:0;background:var(--_k-bg-page)}
18433
+ .knobs-wt-progress{flex:1}.knobs-wt-toc,.knobs-wt-keys{display:none}
18434
+ .knobs-wt-step,.knobs-wt-shead,.knobs-wt-feed{scroll-margin-top:60px}
18435
+ }
18436
+
18437
+ /* ——— media slots. Only a picture's frame (a quiet fill while it loads or letterboxes) and a code
18438
+ block (a well) are bounded; a metric, a callout, a table, a stack and a history sit on the page,
18439
+ set apart by space, hairlines and their own colours. On a card (.knobs-surface) code drops its
18440
+ well for hairlines, so nothing draws a fill and a ring of its own there ——— */
18441
+ .knobs-ui.knobs-wt-slot{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-sibling);min-width:0;container:knobs-wt-slot / inline-size;color:var(--_k-fg);
18442
+ font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;--kw-measure:var(--k-walkthrough-measure,68ch);
18443
+ --kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info))}
18444
+ .knobs-wt-slot-label{display:block;font:var(--_k-weight-label) var(--_k-type-label)/var(--_k-leading-label) var(--_k-font);color:var(--_k-fg-2)}
18445
+ .knobs-wt-body{min-width:0}
18446
+ /* A compare is exactly as tall as its pictures at this width, so there's no letterbox: the two
18447
+ sides edge to edge (side) or one (overlaid), plus its mode bar, the timeline and their two gaps
18448
+ (92 px), and the frame strip with its gap (158 px). */
18449
+ .knobs-wt-fit{width:100%;height:calc(100cqw / 2 / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
18450
+ .knobs-wt-fit[data-overlay]{height:calc(100cqw / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
18451
+ .knobs-wt-fit[data-strip]{--kw-chrome:158px}
18452
+ .knobs-wt-fit[data-settled]{transition:height var(--k-wt-calm-ms,${calm.duration}ms) ${calm.easing}}
18453
+ .knobs-wt-frame{position:relative;width:100%;overflow:hidden;border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_k-fill);aspect-ratio:var(--kw-aspect,16/9)}
18454
+ .knobs-wt-frame>:is(video,img){position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:contain;margin:0;border:0}
18455
+ .knobs-wt-frame[data-natural]{aspect-ratio:auto;background:transparent}
18456
+ .knobs-wt-frame[data-natural]>img{position:static;height:auto}
18457
+ .knobs-wt-code{margin:0;padding:var(--_k-pad-card);max-height:420px;overflow:auto;border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_k-fill);color:var(--_k-fg);
18458
+ font:var(--_k-type-label)/var(--_k-leading-body) var(--_k-mono);white-space:pre;tab-size:2;scrollbar-width:thin}
18459
+ .knobs-surface .knobs-wt-code{padding-inline:0;border-radius:0;background:transparent;box-shadow:inset 0 1px 0 var(--_k-border-subtle),inset 0 -1px 0 var(--_k-border-subtle)}
18460
+ .knobs-wt-code[data-terminal]{white-space:pre-wrap;overflow-wrap:anywhere}
18461
+ .knobs-wt-prompt{color:var(--_k-fg-3);user-select:none;-webkit-user-select:none}
18462
+ .knobs-wt-cmd{color:var(--_k-fg);font-weight:var(--_k-weight-label)}
18463
+ .knobs-wt-out{color:var(--_k-fg-2)}
18464
+ .knobs-wt-metric{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
18465
+ .knobs-wt-mlabel{color:var(--_k-fg-2)}
18466
+ .knobs-wt-mvalue{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--_k-space-sibling);font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
18467
+ .knobs-wt-mfrom{color:var(--_k-fg-3);font-size:var(--_k-type-title);line-height:var(--_k-leading-title)}
18468
+ .knobs-wt-mto{font-size:var(--_k-type-heading);line-height:var(--_k-leading-heading);font-weight:var(--_k-weight-label)}
18469
+ .knobs-wt-mof{color:var(--_k-fg-3)}
18470
+ .knobs-wt-mdelta{--kw-tone:var(--_k-fg-2);font-size:var(--_k-type-small);font-weight:var(--_k-weight-label);color:var(--kw-tone)}
18471
+ .knobs-wt-mdelta[data-tone="good"]{--kw-tone:var(--kw-good)}
18472
+ .knobs-wt-mdelta[data-tone="bad"]{--kw-tone:var(--_k-danger)}
18473
+ .knobs-wt-mtrack{position:relative;height:4px;border-radius:2px;background:color-mix(in srgb,var(--_k-fg) 8%,transparent)}
18474
+ .knobs-wt-mtrack>span{position:absolute;top:0;bottom:0;left:0;border-radius:inherit}
18475
+ .knobs-wt-mtrack>.knobs-wt-mafter{background:var(--_k-accent)}
18476
+ .knobs-wt-mtrack>.knobs-wt-mbefore{background:color-mix(in srgb,var(--_k-fg) 30%,transparent)}
18477
+ .knobs-wt-callout{--kw-tone:var(--kw-info);display:flex;gap:var(--_k-space-sibling);max-width:var(--kw-measure);color:var(--_k-fg)}
18478
+ .knobs-wt-callout::before{content:"";flex:none;width:6px;height:6px;margin-top:7px;border-radius:50%;background:var(--kw-tone)}
18479
+ .knobs-wt-callout[data-tone="good"]{--kw-tone:var(--kw-good)}
18480
+ .knobs-wt-callout[data-tone="warn"]{--kw-tone:var(--_k-warn)}
18481
+ .knobs-wt-callout[data-tone="bad"]{--kw-tone:var(--_k-danger)}
18482
+ .knobs-wt-text{max-width:var(--kw-measure);color:var(--_k-fg-2)}
18483
+ .knobs-wt-hold{border-radius:calc(var(--_k-radius-panel) - 4px);background:var(--_k-fill)}
18484
+ .knobs-wt-failed{display:flex;align-items:center;justify-content:center;min-height:80px;color:var(--_k-fg-3);font-size:var(--_k-type-small)}
18485
+
18486
+ /* ——— data blocks: history, stack, table, chapters. On the step's card, so no card of their own:
18487
+ hairlines and fills only. Tones read the status tokens; hover fades are the panel's own. ——— */
18488
+ :where(.knobs-wt-history,.knobs-wt-stack,.knobs-wt-tablebox,.knobs-wt-chapters){--kw-line:var(--_k-leading-body);--kw-rail:var(--_k-border-strong)}
18489
+ :where(.knobs-wt-slot) [data-tone="good"]{--kw-tone:var(--kw-good)}
18490
+ :where(.knobs-wt-slot) [data-tone="warn"]{--kw-tone:var(--_k-warn)}
18491
+ :where(.knobs-wt-slot) [data-tone="bad"]{--kw-tone:var(--_k-danger)}
18492
+ :where(.knobs-wt-slot) [data-tone="info"]{--kw-tone:var(--kw-info)}
18493
+ :where(.knobs-wt-slot) [data-tone="dim"]{--kw-tone:var(--_k-fg-3)}
18494
+ .knobs-wt-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
18495
+ .knobs-ui .knobs-wt-tag{display:inline-flex;align-items:center;height:20px;padding:0 6px;border-radius:5px;background:var(--_k-fill);color:var(--_k-fg-2);font:var(--_k-weight) var(--_k-type-small)/1 var(--_k-mono);
18496
+ text-decoration:none;white-space:nowrap;transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}
18497
+ .knobs-ui a.knobs-wt-tag:is(:hover,:focus-visible){background:var(--_k-fill-selected);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18498
+
18499
+ /* A history: dates down the left, a spine of tone dots joined by a hairline (broken around each
18500
+ dot), the event beside it. Inferred events take a ring for a dot and a dashed tag. */
18501
+ .knobs-wt-history{--kw-hy:calc(5px + var(--kw-line) / 2);display:flex;flex-direction:column;gap:18px;font-size:var(--_k-type-body);line-height:var(--kw-line);color:var(--_k-fg)}
18502
+ .knobs-wt-hgroup{display:flex;flex-direction:column;gap:6px;min-width:0}
18503
+ .knobs-wt-hhead{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px}
18504
+ .knobs-ui .knobs-wt-hheading{margin:0;font:var(--_k-weight-label) var(--_k-type-small)/var(--_k-leading-small) var(--_k-font);letter-spacing:.04em;text-transform:uppercase;color:var(--_k-fg-2)}
18505
+ .knobs-wt-hsum{color:var(--_k-fg-3);font-size:var(--_k-type-small);font-variant-numeric:tabular-nums}
18506
+ .knobs-wt-hlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
18507
+ .knobs-wt-hev{display:grid;grid-template-columns:var(--kw-hw,11ch) 14px minmax(0,1fr);grid-template-areas:"when spine main";column-gap:10px;align-items:baseline;padding:5px 0}
18508
+ .knobs-wt-hwhen{grid-area:when;display:flex;flex-direction:column;min-width:0;font:var(--_k-weight) var(--_k-type-label)/var(--kw-line) var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-2)}
18509
+ .knobs-wt-hage{color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
18510
+ .knobs-wt-hspine{grid-area:spine;position:relative;align-self:stretch;margin-block:-5px}
18511
+ .knobs-wt-hspine::before,.knobs-wt-hspine::after{content:"";position:absolute;left:6.5px;width:1px;background:var(--kw-rail)}
18512
+ .knobs-wt-hspine::before{top:0;height:calc(var(--kw-hy) - 6px)}
18513
+ .knobs-wt-hspine::after{top:calc(var(--kw-hy) + 6px);bottom:0}
18514
+ .knobs-wt-hev:first-child .knobs-wt-hspine::before,.knobs-wt-hev:last-child .knobs-wt-hspine::after{content:none}
18515
+ .knobs-wt-hdot{position:absolute;left:3.5px;top:calc(var(--kw-hy) - 3.5px);width:7px;height:7px;border-radius:50%;background:var(--kw-tone,var(--_k-fg-3))}
18516
+ .knobs-wt-hev[data-type="release"] .knobs-wt-hdot{border-radius:1.5px;rotate:45deg;scale:.9}
18517
+ .knobs-wt-hev[data-type="audit"] .knobs-wt-hdot{border-radius:1.5px}
18518
+ .knobs-wt-hev[data-provenance="inferred"] .knobs-wt-hdot{background:transparent;box-shadow:inset 0 0 0 1.5px var(--kw-tone,var(--_k-fg-3))}
18519
+ .knobs-wt-hmain{grid-area:main;display:flex;flex-direction:column;gap:2px;min-width:0}
18520
+ .knobs-wt-htitle{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px;font-weight:var(--_k-weight-label);color:var(--_k-fg)}
18521
+ .knobs-wt-prov{padding:0 6px;border-radius:5px;background:var(--_k-fill);color:var(--_k-fg-3);font:var(--_k-weight) var(--_k-type-small)/18px var(--_k-mono);white-space:nowrap}
18522
+ .knobs-wt-hev[data-provenance="inferred"] .knobs-wt-prov{background:transparent;color:var(--_k-warn);outline:1px dashed color-mix(in oklch,var(--_k-warn) 50%,transparent);outline-offset:-1px}
18523
+ .knobs-ui .knobs-wt-hdetail{margin:0;max-width:var(--k-walkthrough-measure,68ch);color:var(--_k-fg-2)}
18524
+ @container knobs-wt-slot (max-width:520px){
18525
+ .knobs-wt-hev{grid-template-columns:14px minmax(0,1fr);grid-template-areas:"spine when" "spine main"}
18526
+ .knobs-wt-hwhen{flex-direction:row;flex-wrap:wrap;align-items:baseline;column-gap:8px}
18183
18527
  }
18184
18528
 
18529
+ /* A stack: the trunk at the top, each PR hanging off the branch it merges into, rails drawn as a
18530
+ git graph (solid, so where two meet they don't darken). A PR with a step goes there on a click. */
18531
+ .knobs-wt-stack{--kw-spt:7px;--kw-spb:7px;--kw-sy:calc(var(--kw-spt) + var(--kw-line) / 2);display:flex;flex-direction:column;font-size:var(--_k-type-body);line-height:var(--kw-line);color:var(--_k-fg)}
18532
+ .knobs-wt-srow{display:grid;grid-template-columns:auto minmax(0,1fr) auto;column-gap:10px;align-items:start;margin:0 -8px;padding:var(--kw-spt) 8px var(--kw-spb);
18533
+ border-radius:max(0px,calc(var(--_k-radius-panel) - 8px));transition:background calc(var(--_k-hover-out) * 1ms)}
18534
+ .knobs-wt-srow[data-trunk]{--kw-spb:0px;grid-template-columns:auto minmax(0,1fr)}
18535
+ .knobs-wt-srow[data-go]{cursor:pointer}
18536
+ .knobs-wt-srow[data-go]:hover{background:var(--_ku-wash);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18537
+ .knobs-wt-lanes{display:flex;align-self:stretch;margin-block:calc(var(--kw-spt) * -1) calc(var(--kw-spb) * -1)}
18538
+ .knobs-wt-lane{position:relative;flex:none;width:16px}
18539
+ .knobs-wt-lane:is([data-rail="pass"],[data-rail="tee"],[data-down])::before{content:"";position:absolute;left:7.5px;top:0;bottom:0;width:1px;background:var(--kw-rail)}
18540
+ .knobs-wt-lane[data-down]::before{top:calc(var(--kw-sy) + 3.5px)}
18541
+ .knobs-wt-lane:is([data-rail="tee"],[data-rail="end"])::after{content:"";position:absolute;left:7.5px;top:0;width:13px;height:calc(var(--kw-sy) + .5px);border:solid var(--kw-rail);border-width:0 0 1px 1px;border-bottom-left-radius:6px}
18542
+ .knobs-wt-lane[data-rail="node"]::after{content:"";position:absolute;left:4.5px;top:calc(var(--kw-sy) - 3.5px);width:7px;height:7px;border-radius:50%;background:var(--_k-fg-3)}
18543
+ .knobs-wt-srow[data-trunk] .knobs-wt-lane[data-rail="node"]::after{background:var(--_k-fg-2)}
18544
+ .knobs-wt-trunk{font:var(--_k-weight-label) var(--_k-type-label)/var(--kw-line) var(--_k-mono);color:var(--_k-fg-2)}
18545
+ .knobs-wt-smain{display:flex;flex-direction:column;gap:1px;min-width:0}
18546
+ .knobs-wt-sline{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 8px}
18547
+ .knobs-wt-sord{font:var(--_k-weight) var(--_k-type-label)/var(--kw-line) var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-3);white-space:nowrap}
18548
+ .knobs-ui .knobs-wt-spr{font:var(--_k-weight-label) var(--_k-type-label)/var(--kw-line) var(--_k-mono);color:var(--_k-fg-2);text-decoration:none;white-space:nowrap}
18549
+ .knobs-ui a.knobs-wt-spr:is(:hover,:focus-visible){color:var(--_k-fg);text-decoration:underline}
18550
+ .knobs-ui .knobs-wt-stitle{font-weight:var(--_k-weight-label);line-height:var(--kw-line);color:var(--_k-fg);text-align:left}
18551
+ .knobs-ui button.knobs-wt-stitle{cursor:pointer;text-decoration:underline;text-decoration-color:transparent;text-underline-offset:3px;transition:text-decoration-color calc(var(--_k-hover-out) * 1ms)}
18552
+ .knobs-ui :is(.knobs-wt-srow[data-go]:hover button.knobs-wt-stitle,button.knobs-wt-stitle:focus-visible){text-decoration-color:color-mix(in oklch,currentColor 45%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18553
+ .knobs-ui .knobs-wt-ssummary{margin:0;max-width:var(--k-walkthrough-measure,68ch);color:var(--_k-fg-2)}
18554
+ .knobs-wt-smeta{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 12px;font:var(--_k-weight) var(--_k-type-small)/var(--kw-line) var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-3)}
18555
+ .knobs-wt-delta{display:inline-flex;gap:6px}
18556
+ .knobs-wt-add{color:var(--_k-diff-add)}
18557
+ .knobs-wt-del{color:var(--_k-diff-del)}
18558
+ .knobs-wt-sstatus{margin-top:calc((var(--kw-line) - 20px) / 2)}
18559
+ .knobs-wt-stack[data-flat] .knobs-wt-srow{grid-template-columns:minmax(0,1fr) auto}
18560
+ .knobs-wt-stack[data-flat] .knobs-wt-srow+.knobs-wt-srow{box-shadow:inset 0 1px 0 var(--_k-border)}
18561
+ @container knobs-wt-slot (max-width:520px){
18562
+ .knobs-wt-srow:not([data-trunk]){grid-template-columns:auto minmax(0,1fr)}
18563
+ .knobs-wt-stack[data-flat] .knobs-wt-srow{grid-template-columns:minmax(0,1fr)}
18564
+ .knobs-wt-sstatus{grid-column:-2;justify-self:start;margin-top:4px}
18565
+ }
18566
+
18567
+ /* A table: a real one, its head stuck while it scrolls, both ways; edges fade where there's more
18568
+ (the top one under the head), and thin bars show on hover. */
18569
+ /* Too narrow for its columns, each row becomes a block: its primary cell titling the rest as term
18570
+ and value, the blocks parted by hairlines. Scrolling sideways is the last resort (stack: never). */
18571
+ .knobs-wt-tablebox{min-width:0}
18572
+ .knobs-wt-tablebox[data-measure] table{width:min-content}
18573
+ .knobs-wt-tstack{display:none;flex-direction:column;min-width:0}
18574
+ .knobs-wt-tablebox[data-stacked]>.knobs-wt-table{display:none}
18575
+ .knobs-wt-tablebox[data-stacked]>.knobs-wt-tstack{display:flex}
18576
+ .knobs-wt-trow{display:flex;flex-direction:column;gap:var(--_k-space-sibling);min-width:0;padding-block:var(--_k-space-title);box-shadow:inset 0 -1px 0 var(--_k-border-subtle)}
18577
+ .knobs-wt-trow:first-child{padding-top:0}
18578
+ .knobs-wt-trow:last-of-type{padding-bottom:0;box-shadow:none}
18579
+ .knobs-wt-tline{display:flex;align-items:baseline;justify-content:space-between;gap:var(--_k-space-title);min-width:0}
18580
+ .knobs-wt-tbadges{flex:none;display:flex;align-items:center;gap:var(--_k-space-inside)}
18581
+ .knobs-wt-ttitle{flex:1 1 auto;min-width:0;color:var(--_k-fg);font-weight:var(--_k-weight-label);overflow-wrap:anywhere}
18582
+ .knobs-wt-ttitle[data-mono]{font-family:var(--_k-mono);font-size:var(--_k-type-label)}
18583
+ .knobs-wt-tterms{display:grid;grid-template-columns:minmax(6ch,max-content) minmax(0,1fr);gap:var(--_k-space-inside) var(--_k-space-title);min-width:0}
18584
+ .knobs-wt-tterms dt{color:var(--_k-fg-3);font-size:var(--_k-type-small)}
18585
+ .knobs-wt-tterms dd{min-width:0;color:var(--_k-fg-2);overflow-wrap:anywhere}
18586
+ .knobs-wt-tterms dd[data-mono]{font-family:var(--_k-mono);font-size:var(--_k-type-label);color:var(--_k-fg)}
18587
+ .knobs-wt-tcaption{padding-top:var(--_k-space-sibling);color:var(--_k-fg-3);font-size:var(--_k-type-small)}
18588
+ @property --kw-fade-l{syntax:"<number>";inherits:false;initial-value:1}
18589
+ @property --kw-fade-r{syntax:"<number>";inherits:false;initial-value:1}
18590
+ @keyframes kw-fade-l{from{--kw-fade-l:1}to{--kw-fade-l:0}}
18591
+ @keyframes kw-fade-r{from{--kw-fade-r:0}to{--kw-fade-r:1}}
18592
+ .knobs-wt-table{max-height:520px;overflow:auto;overscroll-behavior-x:contain;outline:none;scrollbar-width:thin;scrollbar-color:transparent transparent;
18593
+ transition:scrollbar-color calc(var(--_k-hover-out) * 1ms)}
18594
+ .knobs-wt-table:is(:hover,:focus-visible){scrollbar-color:color-mix(in oklch,var(--_k-fg) 28%,transparent) transparent;transition-duration:calc(var(--_k-hover-in) * 1ms)}
18595
+ .knobs-wt-table:focus-visible{box-shadow:inset 0 0 0 2px var(--_k-focus);border-radius:calc(var(--_k-radius-panel) - 4px)}
18596
+ @supports (animation-timeline:scroll()){
18597
+ .knobs-wt-table{animation:knobs-fade-a linear both,knobs-fade-b linear both,kw-fade-l linear both,kw-fade-r linear both;animation-duration:auto;
18598
+ animation-timeline:scroll(self block),scroll(self block),scroll(self inline),scroll(self inline);animation-range:0 24px,calc(100% - 24px) 100%,0 8px,calc(100% - 8px) 100%;
18599
+ --kw-table-mask-y:linear-gradient(to bottom,#000 var(--kw-thead,0px),rgb(0 0 0/var(--knobs-fade-a)) var(--kw-thead,0px),#000 calc(var(--kw-thead,0px) + 20px),#000 calc(100% - 20px),rgb(0 0 0/var(--knobs-fade-b)));
18600
+ --kw-table-mask-x:linear-gradient(to right,rgb(0 0 0/var(--kw-fade-l)),#000 24px,#000 calc(100% - 24px),rgb(0 0 0/var(--kw-fade-r)));
18601
+ -webkit-mask-image:var(--kw-table-mask-y),var(--kw-table-mask-x);mask-image:var(--kw-table-mask-y),var(--kw-table-mask-x);-webkit-mask-composite:source-in;mask-composite:intersect}
18602
+ }
18603
+ .knobs-wt-table table{width:100%;margin:0;border-collapse:separate;border-spacing:0;font-size:var(--_k-type-body);line-height:var(--_k-leading-body);font-variant-numeric:tabular-nums;color:var(--_k-fg-2)}
18604
+ .knobs-wt-table caption{caption-side:bottom;padding-top:8px;text-align:left;color:var(--_k-fg-3);font-size:var(--_k-type-small)}
18605
+ .knobs-wt-table th{position:sticky;top:0;padding:0 12px 7px;text-align:left;vertical-align:bottom;font-weight:var(--_k-weight-label);font-size:var(--_k-type-small);color:var(--_k-fg-3);white-space:nowrap;
18606
+ background:var(--kw-under,var(--_k-pane-bg));box-shadow:inset 0 -1px 0 var(--_k-border)}
18607
+ .knobs-wt-table td{padding:8px 12px;vertical-align:baseline;box-shadow:inset 0 -1px 0 var(--_k-border)}
18608
+ .knobs-wt-table tr:last-child td{box-shadow:none}
18609
+ .knobs-wt-table :is(th,td):first-child{padding-left:0}
18610
+ .knobs-wt-table :is(th,td):last-child{padding-right:0}
18611
+ .knobs-wt-table td:first-child{color:var(--_k-fg)}
18612
+ .knobs-wt-table [data-align="right"]{text-align:right}
18613
+ .knobs-wt-table [data-align="center"]{text-align:center}
18614
+ .knobs-wt-table td[data-mono]{font-family:var(--_k-mono);font-size:var(--_k-type-label);color:var(--_k-fg);white-space:nowrap}
18615
+ .knobs-wt-table td[data-long]{min-width:24ch}
18616
+ .knobs-ui :is(.knobs-wt-table,.knobs-wt-tstack) a.knobs-wt-chip{text-decoration:none}
18617
+ .knobs-wt-ctone{color:var(--kw-tone)}
18618
+
18619
+ /* Chapters: a readout list under a video or compare: number, label, start; the selection pill
18620
+ on the one playing, its rows bled out so their text sits on the media's edge. */
18621
+ .knobs-wt-body:has(>.knobs-wt-chapters){display:flex;flex-direction:column;gap:var(--_k-space-title)}
18622
+ .knobs-wt-chapters{position:relative;display:flex;flex-direction:column;margin:0 calc(var(--_k-space-sibling) * -1)}
18623
+ .knobs-ui .knobs-wt-chapter{position:relative;display:grid;grid-template-columns:2ch minmax(0,1fr) auto;column-gap:var(--_k-space-sibling);align-items:center;height:32px;padding:0 var(--_k-space-sibling);
18624
+ border-radius:var(--_k-radius);color:var(--_k-fg-2);text-align:left;white-space:nowrap;cursor:pointer;transition:color calc(var(--_k-hover-out) * 1ms)}
18625
+ .knobs-ui .knobs-wt-chapter:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
18626
+ .knobs-ui .knobs-wt-chapter:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
18627
+ .knobs-wt-chapter-n{font-size:var(--_k-type-small);color:var(--_k-fg-3)}
18628
+ .knobs-wt-chapter-label{min-width:0;overflow:hidden;${fade2}}
18629
+ .knobs-wt-chapter-time{font:var(--_k-weight) var(--_k-type-small)/1 var(--_k-mono);color:var(--_k-fg-3)}
18630
+ .knobs-pick-copy :is(.knobs-wt-chapter-n,.knobs-wt-chapter-time){color:var(--_k-fg-2)}
18631
+ @media(prefers-reduced-motion:reduce){:is(.knobs-walkthrough,.knobs-wt-slot) :where(*,::before,::after){transition-duration:.15s!important;transition-property:opacity,background,color!important}}
18632
+ @media(prefers-reduced-motion:reduce){.knobs-wt-disc-content{transition:opacity .15s}}
18633
+ `;
18634
+
18185
18635
  // src/ui/walkthrough-media.ts
18186
18636
  function injectWalkthroughStyles(doc) {
18187
18637
  if (doc.querySelector("style[data-knobs-ui-walkthrough]"))
@@ -18709,7 +19159,7 @@ function mediaSlot(container, options) {
18709
19159
  }
18710
19160
  };
18711
19161
  }
18712
- var CHECK3 = svg('<path d="m3.5 8.4 3 3 6-6.6"/>');
19162
+ var CHECK3 = icon("check", { small: true });
18713
19163
 
18714
19164
  // src/ui/walkthrough.ts
18715
19165
  var el2 = (doc, tag, cls) => {
@@ -18727,16 +19177,20 @@ var lookOf2 = (o) => ({
18727
19177
  ...o.preset ? { preset: o.preset } : {},
18728
19178
  ...o.theme ? { theme: o.theme } : {}
18729
19179
  });
18730
- var grid3 = (v) => Math.round(v * 64) / 64;
19180
+ var grid2 = (v) => Math.round(v * 64) / 64;
18731
19181
  var ICON = {
18732
- full: svg('<path d="M3 6.5V3h3.5M13 6.5V3H9.5M3 9.5V13h3.5M13 9.5V13H9.5"/>'),
18733
- exit: svg('<path d="M6.5 3v3.5H3M9.5 3v3.5H13M6.5 13V9.5H3M9.5 13V9.5H13"/>'),
18734
- pen: svg('<path d="M10.5 3.2 12.8 5.5 5.8 12.5 3 13l.5-2.8z"/><path d="M9.3 4.4l2.3 2.3"/>'),
18735
- mic: svg('<rect x="6" y="2.5" width="4" height="7" rx="2"/><path d="M3.8 8a4.2 4.2 0 0 0 8.4 0M8 12.2v1.8"/>'),
19182
+ full: icon("cornersOut"),
19183
+ exit: icon("cornersIn"),
19184
+ pen: icon("pencilSimple"),
19185
+ mic: icon("microphone"),
18736
19186
  stop: svg('<rect x="4.5" y="4.5" width="7" height="7" rx="1.5" fill="currentColor" stroke="none"/>'),
18737
- send: svg('<path d="M8 13V3.5M4 7.5 8 3.5l4 4"/>'),
18738
- close: svg('<path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/>')
19187
+ send: icon("paperPlaneRight"),
19188
+ close: icon("x", { small: true }),
19189
+ back: icon("arrowLeft", { small: true }),
19190
+ out: icon("arrowUpRight", { small: true })
18739
19191
  };
19192
+ var DOCK_INSET = 12;
19193
+ var DOCK_GAP = 12;
18740
19194
  var CALM2 = springMotion(SPRINGS.calm);
18741
19195
  var SMOOTH = springMotion(SPRINGS.smooth);
18742
19196
  var BACK_MS = 4500;
@@ -18869,8 +19323,12 @@ function walkthrough(container, options) {
18869
19323
  let count = null;
18870
19324
  let fill = null;
18871
19325
  let tocEl = null;
18872
- let mark = null;
19326
+ let pill = null;
19327
+ let tocRows = [];
19328
+ let discs = [];
18873
19329
  let full = null;
19330
+ const noteMode = options.noteBar ?? "collapsed";
19331
+ let reserve = noteBarReserve(52, DOCK_INSET, DOCK_GAP);
18874
19332
  const viewOf = (key) => views.find((v) => v.step.key === key);
18875
19333
  const feedback = () => collectFeedback(page, state);
18876
19334
  const markdown = () => feedbackMarkdown(feedback(), page.verdicts);
@@ -18894,9 +19352,6 @@ function walkthrough(container, options) {
18894
19352
  else
18895
19353
  run();
18896
19354
  };
18897
- const pillY = new Spring(0, CALM2.omega, CALM2.zeta, 0.25);
18898
- const pillH = new Spring(0, CALM2.omega, CALM2.zeta, 0.25);
18899
- let pillPlaced = false;
18900
19355
  const scroll = {
18901
19356
  spring: new Spring(0, CALM2.omega, CALM2.zeta, 0.5),
18902
19357
  to: null,
@@ -18904,13 +19359,6 @@ function walkthrough(container, options) {
18904
19359
  };
18905
19360
  const motion = frameLoop(win, (dt) => {
18906
19361
  let more = false;
18907
- if (mark) {
18908
- pillY.step(dt);
18909
- pillH.step(dt);
18910
- mark.style.transform = `translateY(${grid3(pillY.x)}px)`;
18911
- mark.style.height = `${grid3(pillY.x + pillH.x) - grid3(pillY.x)}px`;
18912
- more ||= !pillY.resting || !pillH.resting;
18913
- }
18914
19362
  if (scroll.to && scroll.el) {
18915
19363
  scroll.spring.set(scrollTarget(scroll.to, scroll.el));
18916
19364
  scroll.spring.step(dt);
@@ -18974,21 +19422,12 @@ function walkthrough(container, options) {
18974
19422
  v.glyph.dataset.tone = v.step.ask === "none" ? "none" : verdict ? verdictTone(verdict, page.verdicts.findIndex((x) => x.id === verdict)) : done ? "accent" : "dim";
18975
19423
  };
18976
19424
  const placeMark = () => {
18977
- if (!mark || !tocEl)
19425
+ if (!pill || !tocEl)
18978
19426
  return;
19427
+ pill.update();
18979
19428
  const row = viewOf(current)?.toc;
18980
19429
  if (!row)
18981
- return void mark.removeAttribute("data-on");
18982
- mark.dataset.on = "";
18983
- if (!pillPlaced || reduced()) {
18984
- pillY.snap(row.offsetTop);
18985
- pillH.snap(row.offsetHeight);
18986
- pillPlaced = true;
18987
- } else {
18988
- pillY.set(row.offsetTop);
18989
- pillH.set(row.offsetHeight);
18990
- }
18991
- motion.wake();
19430
+ return;
18992
19431
  const top = row.offsetTop;
18993
19432
  const bottom = top + row.offsetHeight;
18994
19433
  if (top < tocEl.scrollTop + 8)
@@ -19005,6 +19444,9 @@ function walkthrough(container, options) {
19005
19444
  const v = viewOf(key);
19006
19445
  v?.card.setAttribute("data-current", "");
19007
19446
  v?.toc?.setAttribute("aria-current", "step");
19447
+ for (const r of tocRows)
19448
+ if (r.dataset.section)
19449
+ r.toggleAttribute("data-active", r.dataset.section === v?.step.section);
19008
19450
  if (key)
19009
19451
  record({ what: "step", step: key });
19010
19452
  placeMark();
@@ -19021,6 +19463,16 @@ function walkthrough(container, options) {
19021
19463
  else
19022
19464
  break;
19023
19465
  }
19466
+ const s = scrollerOf(root);
19467
+ if (s.scrollTop > 0 && s.scrollTop >= s.scrollHeight - s.clientHeight - 2) {
19468
+ const bottom = (s === doc.scrollingElement ? win.innerHeight : s.getBoundingClientRect().bottom) - reserve;
19469
+ for (let i = views.length - 1;i >= 0; i--) {
19470
+ if (views[i].card.getBoundingClientRect().top < bottom) {
19471
+ pick = views[i].step.key;
19472
+ break;
19473
+ }
19474
+ }
19475
+ }
19024
19476
  setCurrent(pick);
19025
19477
  };
19026
19478
  let scrollRaf = 0;
@@ -19063,15 +19515,20 @@ function walkthrough(container, options) {
19063
19515
  };
19064
19516
  const scrim = el2(doc, "div", "knobs-wt-scrim");
19065
19517
  scrim.addEventListener("click", () => setFull(false));
19066
- const fullRect = () => ({ x: 12, y: 12, w: win.innerWidth - 24, h: win.innerHeight - 24 });
19518
+ const fullRect = () => ({
19519
+ x: DOCK_INSET,
19520
+ y: DOCK_INSET,
19521
+ w: win.innerWidth - DOCK_INSET * 2,
19522
+ h: win.innerHeight - DOCK_INSET - reserve
19523
+ });
19067
19524
  const placeFull = (f) => {
19068
19525
  const a = f.hold.getBoundingClientRect();
19069
19526
  const b = fullRect();
19070
19527
  const k = reduced() ? Math.round(f.p.target) : f.p.x;
19071
- const left = grid3(a.left + (b.x - a.left) * k);
19072
- const top = grid3(a.top + (b.y - a.top) * k);
19073
- const right = grid3(a.right + (b.x + b.w - a.right) * k);
19074
- const bottom = grid3(a.bottom + (b.y + b.h - a.bottom) * k);
19528
+ const left = grid2(a.left + (b.x - a.left) * k);
19529
+ const top = grid2(a.top + (b.y - a.top) * k);
19530
+ const right = grid2(a.right + (b.x + b.w - a.right) * k);
19531
+ const bottom = grid2(a.bottom + (b.y + b.h - a.bottom) * k);
19075
19532
  const s = f.v.card.style;
19076
19533
  s.left = `${left}px`;
19077
19534
  s.top = `${top}px`;
@@ -19208,6 +19665,7 @@ function walkthrough(container, options) {
19208
19665
  ink.on = true;
19209
19666
  ink.layer.dataset.on = "";
19210
19667
  root.dataset.inking = "";
19668
+ bar.setOpen(true);
19211
19669
  bar.sync();
19212
19670
  };
19213
19671
  const voice = {
@@ -19247,6 +19705,7 @@ function walkthrough(container, options) {
19247
19705
  });
19248
19706
  voice.recorder = rec;
19249
19707
  voice.started = performance.now();
19708
+ bar.setOpen(true);
19250
19709
  rec.start();
19251
19710
  voice.timer = win.setInterval(() => bar.sync(), 250);
19252
19711
  bar.sync();
@@ -19273,7 +19732,10 @@ function walkthrough(container, options) {
19273
19732
  if (!v)
19274
19733
  return;
19275
19734
  backTo = key;
19276
- backPill.textContent = `← Back to ${v.step.number}`;
19735
+ backPill.innerHTML = ICON.back;
19736
+ const words = el2(doc, "span", "knobs-wt-back-label");
19737
+ words.textContent = `Back to ${plainMarkdown(v.step.title)}`;
19738
+ backPill.append(words);
19277
19739
  backPill.dataset.on = "";
19278
19740
  win.clearTimeout(backTimer);
19279
19741
  backTimer = win.setTimeout(hideBack, BACK_MS);
@@ -19305,7 +19767,41 @@ function walkthrough(container, options) {
19305
19767
  sendBtn.dataset.send = "";
19306
19768
  row.append(input, attach, fullBtn, penBtn, micBtn, sendBtn);
19307
19769
  box.append(on, row);
19308
- dock.append(backPill, box);
19770
+ const launch = el2(doc, "button", "knobs-wt-launch");
19771
+ launch.type = "button";
19772
+ launch.innerHTML = ICON.pen;
19773
+ launch.append("Note");
19774
+ const kbd = el2(doc, "kbd");
19775
+ kbd.textContent = "n";
19776
+ launch.append(kbd);
19777
+ launch.setAttribute("aria-label", "Write a note (n)");
19778
+ launch.addEventListener("click", () => setOpen(true, true));
19779
+ dock.dataset.mode = noteMode;
19780
+ dock.append(backPill, box, launch);
19781
+ let open = noteMode !== "collapsed";
19782
+ const setOpen = (on, focus = false) => {
19783
+ if (noteMode === "collapsed" && on !== open) {
19784
+ open = on;
19785
+ dock.toggleAttribute("data-open", on);
19786
+ box.inert = !on;
19787
+ launch.inert = on;
19788
+ syncReserve();
19789
+ }
19790
+ if (on && focus)
19791
+ input.focus({ preventScroll: true });
19792
+ };
19793
+ dock.toggleAttribute("data-open", open);
19794
+ box.inert = !open;
19795
+ launch.inert = open;
19796
+ const empty = () => !input.value.trim() && !voice.pending && !voice.recorder && !ink.on && !ink.paths.length;
19797
+ const onOutside = (e) => {
19798
+ if (open && noteMode === "collapsed" && !dock.contains(e.target) && empty())
19799
+ setOpen(false);
19800
+ };
19801
+ doc.addEventListener("pointerdown", onOutside, true);
19802
+ const ro = "ResizeObserver" in win ? new win.ResizeObserver(() => syncReserve()) : null;
19803
+ ro?.observe(box);
19804
+ ro?.observe(launch);
19309
19805
  const fit = () => {
19310
19806
  input.style.height = "auto";
19311
19807
  input.style.height = `${Math.min(140, input.scrollHeight)}px`;
@@ -19321,6 +19817,8 @@ function walkthrough(container, options) {
19321
19817
  } else if (e.key === "Escape") {
19322
19818
  e.preventDefault();
19323
19819
  input.blur();
19820
+ if (empty())
19821
+ setOpen(false);
19324
19822
  viewOf(current)?.card.focus({ preventScroll: true });
19325
19823
  }
19326
19824
  });
@@ -19340,13 +19838,13 @@ function walkthrough(container, options) {
19340
19838
  const sync = () => {
19341
19839
  const v = viewOf(current);
19342
19840
  const ctx = describeMedia(mediaContext(v));
19343
- const where = v ? `${v.step.number} ${v.step.title}` : "General note";
19841
+ const where = v ? plainMarkdown(v.step.title) : "General note";
19344
19842
  const text = ctx ? `${where} · ${ctx}` : where;
19345
19843
  if (text !== lastOn) {
19346
19844
  on.textContent = text;
19347
19845
  lastOn = text;
19348
19846
  }
19349
- input.placeholder = v ? `Note on ${v.step.number}… (n)` : "A note on the whole page… (n)";
19847
+ input.placeholder = v ? "A note on this step… (n)" : "A note on the whole page… (n)";
19350
19848
  const attached = `${voice.pending?.url ?? ""}|${ink.paths.length}`;
19351
19849
  if (attached !== lastAttached) {
19352
19850
  lastAttached = attached;
@@ -19385,14 +19883,45 @@ function walkthrough(container, options) {
19385
19883
  };
19386
19884
  return {
19387
19885
  dock,
19886
+ box,
19887
+ launch,
19388
19888
  input,
19389
19889
  sync,
19890
+ get open() {
19891
+ return open;
19892
+ },
19893
+ setOpen,
19894
+ empty,
19895
+ unobserve: () => {
19896
+ ro?.disconnect();
19897
+ doc.removeEventListener("pointerdown", onOutside, true);
19898
+ },
19390
19899
  clear() {
19391
19900
  input.value = "";
19392
19901
  fit();
19393
19902
  }
19394
19903
  };
19395
19904
  })();
19905
+ let padded = null;
19906
+ function syncReserve() {
19907
+ if (disposed)
19908
+ return;
19909
+ const h = (bar.open ? bar.box : bar.launch).offsetHeight;
19910
+ if (noteMode === "hidden")
19911
+ reserve = 0;
19912
+ else if (h)
19913
+ reserve = noteBarReserve(h, DOCK_INSET, DOCK_GAP);
19914
+ root.style.setProperty("--kw-reserve", `${reserve}px`);
19915
+ const s = scrollerOf(root);
19916
+ if (padded?.el !== s) {
19917
+ if (padded)
19918
+ padded.el.style.scrollPaddingBottom = padded.was;
19919
+ padded = { el: s, was: s.style.scrollPaddingBottom };
19920
+ }
19921
+ s.style.scrollPaddingBottom = `${reserve}px`;
19922
+ if (full)
19923
+ placeFull(full);
19924
+ }
19396
19925
  const ticker = win.setInterval(() => !doc.hidden && bar.sync(), 500);
19397
19926
  const send = (text = bar.input.value) => {
19398
19927
  if (voice.recorder)
@@ -19428,6 +19957,10 @@ function walkthrough(container, options) {
19428
19957
  syncProgress();
19429
19958
  emit();
19430
19959
  bar.sync();
19960
+ if (noteMode === "collapsed") {
19961
+ bar.input.blur();
19962
+ bar.setOpen(false);
19963
+ }
19431
19964
  if (v && options.advance !== false) {
19432
19965
  const to = adjacentStep(views.map((x) => x.step.key), v.step.key, 1);
19433
19966
  if (to && to !== v.step.key) {
@@ -19435,7 +19968,8 @@ function walkthrough(container, options) {
19435
19968
  setFull(false);
19436
19969
  go(to);
19437
19970
  showBack(v.step.key);
19438
- bar.input.focus({ preventScroll: true });
19971
+ if (noteMode === "docked")
19972
+ bar.input.focus({ preventScroll: true });
19439
19973
  }
19440
19974
  }
19441
19975
  };
@@ -19522,22 +20056,105 @@ function walkthrough(container, options) {
19522
20056
  row.addEventListener("transitionend", done, { once: true });
19523
20057
  win.setTimeout(done, 600);
19524
20058
  };
20059
+ const metaLine = (cls) => el2(doc, "div", `knobs-wt-metaline ${cls}`);
20060
+ const metaText = (line, text, cls) => {
20061
+ const s = el2(doc, "span", cls);
20062
+ s.textContent = text;
20063
+ line.append(s);
20064
+ return s;
20065
+ };
20066
+ const metaLink = (line, text, href) => {
20067
+ const a = el2(doc, "a");
20068
+ a.href = href;
20069
+ a.target = "_blank";
20070
+ a.rel = "noreferrer";
20071
+ a.textContent = text;
20072
+ a.insertAdjacentHTML("beforeend", ICON.out);
20073
+ line.append(a);
20074
+ return a;
20075
+ };
20076
+ const feedItem = (f, now) => {
20077
+ const li = el2(doc, "li", "knobs-wt-fi");
20078
+ li.dataset.kind = f.kind;
20079
+ const q = el2(doc, "div", "knobs-wt-fi-q");
20080
+ const meta = metaLine("knobs-wt-fi-meta");
20081
+ if (f.at !== null) {
20082
+ const when = metaText(meta, ago(f.at, now));
20083
+ when.title = new Date(f.at).toLocaleString();
20084
+ } else
20085
+ metaText(meta, f.kind === "qa" ? "Answer" : "Update");
20086
+ const step = f.step ? page.steps.find((s) => s.key === f.step) : undefined;
20087
+ if (step) {
20088
+ const section = page.sections.find((s) => s.id === step.section);
20089
+ const b = el2(doc, "button", "knobs-wt-fi-step");
20090
+ b.type = "button";
20091
+ b.textContent = section?.link?.label ?? plainMarkdown(step.title);
20092
+ b.title = `Go to “${plainMarkdown(step.title)}”`;
20093
+ b.addEventListener("click", () => go(step.key));
20094
+ meta.append(b);
20095
+ }
20096
+ if (f.status) {
20097
+ const id = f.status.toLowerCase();
20098
+ const shipped = /^(ship|shipped|merged|done)$/.test(id);
20099
+ const changes = /^(changes?|changes queued)$/.test(id);
20100
+ const s = metaText(meta, shipped ? "Shipped" : changes ? "Changes queued" : f.status);
20101
+ s.dataset.status = shipped ? "on" : changes ? "warn" : "";
20102
+ }
20103
+ q.append(meta);
20104
+ if (f.q)
20105
+ q.append(prose2(doc, "p", "knobs-wt-fi-ask", f.q));
20106
+ else
20107
+ q.append(prose2(doc, "p", "knobs-wt-fi-text", f.text));
20108
+ li.append(q);
20109
+ if (f.q || f.trail.length) {
20110
+ const a = el2(doc, "div", "knobs-wt-fi-a");
20111
+ if (f.q)
20112
+ a.append(prose2(doc, "div", "knobs-wt-fi-answer", f.text));
20113
+ if (f.trail.length) {
20114
+ const list = el2(doc, "ol", "knobs-wt-trail");
20115
+ for (const line of f.trail) {
20116
+ const i = line.indexOf(" · ");
20117
+ const row = el2(doc, "li");
20118
+ const words = el2(doc, "span", "knobs-wt-trail-text");
20119
+ words.textContent = i < 0 ? line : line.slice(i + 3);
20120
+ row.append(words);
20121
+ if (i >= 0) {
20122
+ const at = el2(doc, "span", "knobs-wt-trail-at");
20123
+ at.textContent = line.slice(0, i);
20124
+ row.append(at);
20125
+ }
20126
+ list.append(row);
20127
+ }
20128
+ discs.push(disclosure(a, { label: "Context", count: f.trail.length, content: list }));
20129
+ }
20130
+ li.append(a);
20131
+ }
20132
+ return li;
20133
+ };
19525
20134
  const build = () => {
19526
20135
  for (const v of views) {
19527
20136
  v.slots.forEach((s) => s.dispose());
19528
20137
  v.taste?.dispose();
19529
20138
  }
20139
+ for (const d of discs)
20140
+ d.dispose();
20141
+ discs = [];
20142
+ pill?.dispose();
20143
+ pill = null;
20144
+ tocRows = [];
19530
20145
  if (full) {
19531
20146
  full.p.snap(0);
19532
20147
  closeFull();
19533
20148
  }
19534
20149
  clearInk();
19535
20150
  views = [];
19536
- pillPlaced = false;
19537
20151
  root.textContent = "";
19538
20152
  root.removeAttribute("data-settled");
20153
+ root.dataset.rail = options.rail === false ? "none" : options.rail === "left" ? "left" : "right";
20154
+ root.toggleAttribute("data-cards", options.cards === true);
19539
20155
  const layout = el2(doc, "div", "knobs-wt-layout");
19540
20156
  const main = el2(doc, "div", "knobs-wt-main");
20157
+ const flow = el2(doc, "div", "knobs-wt-flow");
19541
20158
  const head = el2(doc, "header", "knobs-wt-head");
19542
20159
  const h1 = el2(doc, "h1", "knobs-wt-title");
19543
20160
  h1.textContent = page.title;
@@ -19564,42 +20181,27 @@ function walkthrough(container, options) {
19564
20181
  }
19565
20182
  head.append(meta);
19566
20183
  }
19567
- main.append(head);
20184
+ flow.append(head);
20185
+ let feedEl = null;
19568
20186
  if (page.feed.length) {
19569
- const feed = el2(doc, "section", "knobs-wt-feed");
19570
- feed.setAttribute("aria-label", "Latest");
19571
- const list = el2(doc, "ol", "knobs-wt-feed-list knobs-scroll-fade");
20187
+ feedEl = el2(doc, "section", "knobs-wt-feed");
20188
+ feedEl.id = "wt-latest";
20189
+ feedEl.setAttribute("aria-label", "Updates and answers");
20190
+ const fh = el2(doc, "header", "knobs-wt-feed-head");
20191
+ const h = el2(doc, "h2", "knobs-wt-feed-title");
20192
+ h.textContent = page.feed.some((f) => f.kind === "qa") ? "Updates & Q&A" : "Updates";
20193
+ const n = el2(doc, "span", "knobs-wt-feed-count");
20194
+ n.textContent = String(page.feed.length);
20195
+ fh.append(h, n);
20196
+ const list = el2(doc, "ol", "knobs-wt-feed-list");
19572
20197
  const now = Date.now();
19573
- for (const f of page.feed) {
19574
- const li = el2(doc, "li", "knobs-wt-fi");
19575
- li.dataset.kind = f.kind;
19576
- const when = el2(doc, "span", "knobs-wt-fi-when");
19577
- when.textContent = f.at === null ? "" : ago(f.at, now);
19578
- if (f.at !== null)
19579
- when.title = new Date(f.at).toLocaleString();
19580
- const kind = el2(doc, "span", "knobs-wt-fi-kind");
19581
- kind.textContent = f.kind === "qa" ? "Answer" : "Update";
19582
- const body = el2(doc, "div", "knobs-wt-fi-body");
19583
- if (f.q)
19584
- body.append(prose2(doc, "p", "knobs-wt-fi-q", f.q));
19585
- body.append(prose2(doc, "p", "knobs-wt-fi-text", f.text));
19586
- li.append(when, kind, body);
19587
- if (f.step) {
19588
- const target = f.step;
19589
- const link = el2(doc, "button", "knobs-wt-fi-step");
19590
- link.type = "button";
19591
- link.textContent = page.steps.find((s) => s.key === target)?.number ?? "";
19592
- link.title = "Go to the step";
19593
- link.addEventListener("click", () => go(target));
19594
- li.append(link);
19595
- }
19596
- list.append(li);
19597
- }
19598
- feed.append(list);
19599
- main.append(feed);
20198
+ for (const f of page.feed)
20199
+ list.append(feedItem(f, now));
20200
+ feedEl.append(fh, list);
20201
+ flow.append(feedEl);
19600
20202
  }
19601
20203
  let rail = null;
19602
- count = fill = tocEl = mark = null;
20204
+ count = fill = tocEl = null;
19603
20205
  if (options.rail !== false) {
19604
20206
  rail = el2(doc, "aside", "knobs-wt-rail");
19605
20207
  rail.setAttribute("aria-label", "Progress");
@@ -19611,8 +20213,17 @@ function walkthrough(container, options) {
19611
20213
  progress.append(count, meter);
19612
20214
  tocEl = el2(doc, "nav", "knobs-wt-toc");
19613
20215
  tocEl.setAttribute("aria-label", "Contents");
19614
- mark = el2(doc, "div", "knobs-wt-toc-mark");
19615
- tocEl.append(mark);
20216
+ const ns = page.sections.map((s) => s.kicker && s.link ? s.link.label : "");
20217
+ const nw = Math.max(0, ...ns.map((n) => n.length));
20218
+ tocEl.style.setProperty("--kw-toc-n", nw ? `${nw + 0.5}ch` : "0px");
20219
+ if (!nw)
20220
+ tocEl.dataset.bare = "";
20221
+ if (feedEl) {
20222
+ const group = el2(doc, "div", "knobs-wt-toc-group");
20223
+ const target = feedEl;
20224
+ group.append(tocRow("Latest", "", String(page.feed.length), false, () => target.scrollIntoView({ block: "start", behavior: reduced() ? "auto" : "smooth" })));
20225
+ tocEl.append(group);
20226
+ }
19616
20227
  rail.append(progress, tocEl, actions(true));
19617
20228
  if (options.keys !== false) {
19618
20229
  const keys = el2(doc, "div", "knobs-wt-keys");
@@ -19624,45 +20235,44 @@ function walkthrough(container, options) {
19624
20235
  const sec = el2(doc, "section", "knobs-wt-section");
19625
20236
  sec.id = `wt-${section.id}`;
19626
20237
  const sh = el2(doc, "header", "knobs-wt-shead");
19627
- if (section.link || section.status) {
19628
- const kicker = el2(doc, "div", "knobs-wt-kicker");
19629
- if (section.link) {
19630
- const a = el2(doc, "a");
19631
- a.href = section.link.href;
19632
- a.target = "_blank";
19633
- a.rel = "noreferrer";
19634
- a.textContent = `${section.link.label} ↗`;
19635
- kicker.append(a);
19636
- }
20238
+ if (section.kicker || section.link || section.status) {
20239
+ const meta = metaLine("knobs-wt-kicker");
20240
+ if (section.kicker)
20241
+ metaText(meta, section.kicker, "knobs-wt-kicker-lead");
20242
+ if (section.link)
20243
+ metaLink(meta, section.link.label, section.link.href);
19637
20244
  if (section.status) {
19638
20245
  const s = statusLook(section.status);
19639
- const chip = el2(doc, "span", "knobs-wt-chip");
19640
- chip.dataset.tone = s.tone;
19641
- chip.textContent = s.label;
19642
- kicker.append(chip);
20246
+ metaText(meta, s.label, "knobs-wt-status").dataset.tone = s.tone;
19643
20247
  }
19644
- sh.append(kicker);
20248
+ sh.append(meta);
19645
20249
  }
19646
- const h2 = el2(doc, "h2", "knobs-wt-stitle");
19647
- h2.textContent = section.title;
20250
+ const h2 = el2(doc, "h2", "knobs-wt-heading");
20251
+ h2.innerHTML = inlineMarkdown(section.title);
19648
20252
  sh.append(h2);
20253
+ const intro = el2(doc, "div", "knobs-wt-sintro");
20254
+ intro.append(sh);
19649
20255
  if (section.summary)
19650
- sh.append(prose2(doc, "p", "knobs-wt-ssum", section.summary));
19651
- sec.append(sh);
20256
+ intro.append(prose2(doc, "p", "knobs-wt-ssum", section.summary));
20257
+ sec.append(intro);
20258
+ let group = null;
19652
20259
  if (tocEl) {
19653
- const t = el2(doc, "div", "knobs-wt-toc-section");
19654
- t.textContent = section.link ? `${section.link.label} ${section.title}` : section.title;
19655
- t.title = section.title;
19656
- tocEl.append(t);
20260
+ group = el2(doc, "div", "knobs-wt-toc-group");
20261
+ const first = section.steps[0]?.key;
20262
+ const n = section.kicker && section.link ? section.link.label : "";
20263
+ group.append(tocRow(plainMarkdown(section.title), n, "", false, () => first ? go(first) : undefined, section.id));
20264
+ tocEl.append(group);
19657
20265
  }
19658
20266
  for (const step of section.steps) {
19659
- const v = stepView(step);
20267
+ const v = stepView(step, group);
19660
20268
  sec.append(v.card);
19661
20269
  views.push(v);
19662
20270
  }
19663
- main.append(sec);
20271
+ flow.append(sec);
19664
20272
  }
19665
- main.append(bar.dock);
20273
+ main.append(flow);
20274
+ if (noteMode !== "hidden")
20275
+ main.append(bar.dock);
19666
20276
  if (rail)
19667
20277
  layout.append(rail);
19668
20278
  layout.append(main);
@@ -19672,16 +20282,40 @@ function walkthrough(container, options) {
19672
20282
  for (const n of notesOn(state, v.step.key))
19673
20283
  renderNote(v, n);
19674
20284
  }
20285
+ if (tocEl)
20286
+ pill = selectionPill(tocEl, {
20287
+ selected: () => viewOf(current)?.toc ?? null,
20288
+ rows: () => tocRows
20289
+ });
19675
20290
  syncProgress();
19676
20291
  current = null;
19677
20292
  fromScroll();
19678
20293
  placeMark();
19679
20294
  bar.sync();
20295
+ syncReserve();
19680
20296
  win.requestAnimationFrame(() => win.requestAnimationFrame(() => {
19681
20297
  if (!disposed && !reduced())
19682
20298
  root.dataset.settled = "";
19683
20299
  }));
19684
20300
  };
20301
+ const tocRow = (label, n, end, sub, run, section) => {
20302
+ const b = el2(doc, "button", sub ? "knobs-wt-toc-row knobs-wt-toc-sub" : "knobs-wt-toc-row");
20303
+ b.type = "button";
20304
+ if (section)
20305
+ b.dataset.section = section;
20306
+ const ncol = el2(doc, "span", "knobs-wt-toc-n");
20307
+ ncol.textContent = n;
20308
+ const t = el2(doc, "span", "knobs-wt-toc-label");
20309
+ t.textContent = label;
20310
+ const e = el2(doc, "span", "knobs-wt-toc-end");
20311
+ e.textContent = end;
20312
+ b.title = label;
20313
+ b.append(ncol, t, e);
20314
+ b.addEventListener("mousedown", (ev) => ev.preventDefault());
20315
+ b.addEventListener("click", run);
20316
+ tocRows.push(b);
20317
+ return b;
20318
+ };
19685
20319
  const actions = (rail) => {
19686
20320
  const row = el2(doc, "div", "knobs-wt-actions");
19687
20321
  const btn = (text, run, primary = false) => {
@@ -19722,30 +20356,38 @@ function walkthrough(container, options) {
19722
20356
  toast("Couldn't reach the clipboard", { tone: "warn", look, doc });
19723
20357
  }
19724
20358
  };
19725
- const stepView = (step) => {
19726
- const card = el2(doc, "article", "knobs-wt-step knobs-surface");
20359
+ const stepView = (step, group) => {
20360
+ const card = el2(doc, "article", options.cards ? "knobs-wt-step knobs-surface" : "knobs-wt-step");
19727
20361
  card.id = `wt-${step.key.replace("/", "--")}`;
19728
20362
  card.tabIndex = -1;
19729
20363
  card.dataset.key = step.key;
19730
- card.setAttribute("aria-label", `${step.number} ${step.title}`);
20364
+ card.setAttribute("aria-label", plainMarkdown(step.title));
19731
20365
  const head = el2(doc, "header", "knobs-wt-step-head");
19732
- const num = el2(doc, "span", "knobs-wt-num");
19733
- num.textContent = step.number;
19734
20366
  const h3 = el2(doc, "h3", "knobs-wt-stepTitle");
19735
- h3.textContent = step.title;
19736
- head.append(num, h3);
19737
- card.append(head);
20367
+ h3.innerHTML = inlineMarkdown(step.title);
20368
+ head.append(h3);
19738
20369
  if (step.caption)
19739
- card.append(prose2(doc, "p", "knobs-wt-caption", step.caption));
20370
+ head.append(prose2(doc, "p", "knobs-wt-caption", step.caption));
20371
+ card.append(head);
19740
20372
  const slots = [];
19741
20373
  const onEvent = (e) => record({ ...e, step: step.key });
19742
20374
  let media = null;
19743
20375
  if (step.media.length) {
19744
- media = el2(doc, "div", "knobs-wt-media");
19745
- media.style.setProperty("--kw-n", String(Math.min(step.media.length, 3)));
19746
- for (const m of step.media)
19747
- slots.push(mediaSlot(media, { ...look, media: m, onEvent, steps: stepLinks }));
20376
+ const grid = (list) => {
20377
+ const g = el2(doc, "div", "knobs-wt-media");
20378
+ g.style.setProperty("--kw-n", String(Math.min(list.length, step.columns)));
20379
+ for (const m of list)
20380
+ slots.push(mediaSlot(g, { ...look, media: m, onEvent, steps: stepLinks }));
20381
+ return g;
20382
+ };
20383
+ const [first, ...rest] = step.media;
20384
+ media = grid(step.more ? [first] : step.media);
19748
20385
  card.append(media);
20386
+ if (step.more && rest.length) {
20387
+ const holder = el2(doc, "div", "knobs-wt-more");
20388
+ discs.push(disclosure(holder, { label: step.more, count: rest.length, content: grid(rest) }));
20389
+ card.append(holder);
20390
+ }
19749
20391
  }
19750
20392
  let taste = null;
19751
20393
  if (step.question)
@@ -19770,16 +20412,11 @@ function walkthrough(container, options) {
19770
20412
  });
19771
20413
  let toc = null;
19772
20414
  let glyph = null;
19773
- if (tocEl) {
19774
- toc = el2(doc, "button", "knobs-wt-toc-step");
19775
- toc.type = "button";
20415
+ if (group) {
20416
+ toc = tocRow(plainMarkdown(step.title), "", "", true, () => go(step.key));
19776
20417
  glyph = el2(doc, "span", "knobs-wt-toc-glyph");
19777
- const label = el2(doc, "span", "knobs-wt-toc-label");
19778
- label.textContent = step.title;
19779
- toc.title = `${step.number} ${step.title}`;
19780
- toc.append(glyph, label);
19781
- toc.addEventListener("click", () => go(step.key));
19782
- tocEl.append(toc);
20418
+ toc.querySelector(".knobs-wt-toc-end").append(glyph);
20419
+ group.append(toc);
19783
20420
  }
19784
20421
  return { step, card, media, notes, toc, glyph, taste, slots };
19785
20422
  };
@@ -19838,15 +20475,21 @@ function walkthrough(container, options) {
19838
20475
  setFull(false);
19839
20476
  return go(next);
19840
20477
  }
20478
+ if ((key === "Home" || key === "End") && views.length) {
20479
+ claim();
20480
+ if (full)
20481
+ setFull(false);
20482
+ return go(views[key === "Home" ? 0 : views.length - 1].step.key);
20483
+ }
19841
20484
  if (key === "ArrowLeft" || key === "ArrowRight") {
19842
20485
  const d = (key === "ArrowLeft" ? -1 : 1) * (e.shiftKey ? 2 : 0.5);
19843
20486
  if (seekBy(d))
19844
20487
  claim();
19845
20488
  return;
19846
20489
  }
19847
- if (key === "n") {
20490
+ if (key === "n" && noteMode !== "hidden") {
19848
20491
  claim();
19849
- bar.input.focus({ preventScroll: true });
20492
+ bar.setOpen(true, true);
19850
20493
  return;
19851
20494
  }
19852
20495
  const v = viewOf(current);
@@ -19925,6 +20568,12 @@ function walkthrough(container, options) {
19925
20568
  win.removeEventListener(type, stopScroll);
19926
20569
  doc.removeEventListener("keydown", onKey);
19927
20570
  unregisterHints();
20571
+ bar.unobserve();
20572
+ pill?.dispose();
20573
+ for (const d of discs)
20574
+ d.dispose();
20575
+ if (padded)
20576
+ padded.el.style.scrollPaddingBottom = padded.was;
19928
20577
  for (const v of views) {
19929
20578
  v.slots.forEach((s) => s.dispose());
19930
20579
  v.taste?.dispose();