yukey

Rename the sw prefix to yk everywhere

commit d2b3f3bb719a80363c00bdda1bebd7e294eb5800 · parent 84fca238b7 · browse this tree
Ximianth, 2026-10-10 06:07:45 UTC

Tags, CSS custom properties, events, classes, attributes, TS identifiers,
gallery demo elements (swg- -> ykg-), hash routes, and prose mentions.
The gallery shell now calls itself the yukey gallery.

Two things beyond the bare substitution:
- "an sw-" became "a yk-" in prose, since the article follows the sound.
- The button e2e scenario's CLI name is "yk" (was "sw"), matching its export.
diff --git a/packages/e2e/main.ts b/packages/e2e/main.ts
index b457e8c..cc6484a 100644
--- a/packages/e2e/main.ts
+++ b/packages/e2e/main.ts
@@ -9,9 +9,9 @@ import { Harness } from "./harness.ts";
 import { ipDrive } from "./scenarios/ip-drive.ts";
 import { lcDrive } from "./scenarios/lc-drive.ts";
 import { mcDrive } from "./scenarios/mc-drive.ts";
-import { swDrive } from "./scenarios/yk-button-drive.ts";
+import { ykDrive } from "./scenarios/yk-button-drive.ts";
 
-const ALL: Scenario[] = [mcDrive, swDrive, ipDrive, lcDrive];
+const ALL: Scenario[] = [mcDrive, ykDrive, ipDrive, lcDrive];
 
 async function main(): Promise<void> {
 	const wanted = process.argv.slice(2);
diff --git a/packages/e2e/scenarios/ip-drive.ts b/packages/e2e/scenarios/ip-drive.ts
index 6dae682..d464d70 100644
--- a/packages/e2e/scenarios/ip-drive.ts
+++ b/packages/e2e/scenarios/ip-drive.ts
@@ -1,7 +1,7 @@
-// Drives the sw-infopop gallery entry with TRUSTED input via CDP: keys via
+// Drives the yk-infopop gallery entry with TRUSTED input via CDP: keys via
 // Input.dispatchKeyEvent and real mouse movement via Input.dispatchMouseEvent
 // (synthetic pointerover can't prove real hover).  Covers: keyboard i on every
-// row flavor (sw-button's own claim, the plain-row/slider/aux data-info
+// row flavor (yk-button's own claim, the plain-row/slider/aux data-info
 // fallback), the i toggle (second i on the same anchor dismisses; scope is one
 // keypress), hover raising the SAME content, pin precedence over hover (incl.
 // i upgrading a hover popup to pinned), dismissal (nav keys close AND navigate,
@@ -16,9 +16,9 @@ import { openGallery } from "../gallery.ts";
 // column's focused row and log, and the aria reflection on the current anchor.
 const STATE_JS = `
 (() => {
-  const shell = document.querySelector("sw-gallery").shadowRoot;
-  const pop = shell.querySelector("sw-infopop");
-  const col = shell.querySelector("swg-infopop-column")?.shadowRoot;
+  const shell = document.querySelector("yk-gallery").shadowRoot;
+  const pop = shell.querySelector("yk-infopop");
+  const col = shell.querySelector("ykg-infopop-column")?.shadowRoot;
   const rect = pop.getBoundingClientRect();
   const anchorRect = pop.anchor?.getBoundingClientRect() ?? null;
   return JSON.stringify({
@@ -71,12 +71,12 @@ async function run(ctx: ScenarioContext): Promise<void> {
 		return JSON.parse(box as string);
 	};
 
-	const COL = "document.querySelector('sw-gallery').shadowRoot.querySelector('swg-infopop-column').shadowRoot";
+	const COL = "document.querySelector('yk-gallery').shadowRoot.querySelector('ykg-infopop-column').shadowRoot";
 
 	await openGallery(
 		ctx,
-		"#sw-infopop",
-		"document.readyState === 'complete' && !!document.querySelector('sw-gallery')?.shadowRoot?.querySelector('swg-infopop-column')",
+		"#yk-infopop",
+		"document.readyState === 'complete' && !!document.querySelector('yk-gallery')?.shadowRoot?.querySelector('ykg-infopop-column')",
 	);
 
 	// Park the mouse in dead space so no hover state lingers between phases.
@@ -99,8 +99,8 @@ async function run(ctx: ScenarioContext): Promise<void> {
 
 	await cdp.key("i");
 	s = await st();
-	check("i-on-sw-button-pins", s.mode === "pinned" && s.visible, JSON.stringify(s));
-	check("i-on-sw-button-content", s.text.includes("an sw-button answers i itself"), s.text);
+	check("i-on-yk-button-pins", s.mode === "pinned" && s.visible, JSON.stringify(s));
+	check("i-on-yk-button-content", s.text.includes("a yk-button answers i itself"), s.text);
 	check("aria-reflection-wired", s.anchorAriaWired === true, JSON.stringify(s.anchorAriaWired));
 	check(
 		"popup-below-anchor",
@@ -112,13 +112,13 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// The toggle: i again on the same anchor dismisses (no blink-reopen)...
 	await cdp.key("i");
 	s = await st();
-	check("i-toggles-off-sw-button", s.mode === "closed", JSON.stringify(s.mode));
+	check("i-toggles-off-yk-button", s.mode === "closed", JSON.stringify(s.mode));
 	// ... and a fresh press raises it right back (suppression is one keypress).
 	await cdp.key("i");
 	s = await st();
 	check(
-		"i-reopens-sw-button",
-		s.mode === "pinned" && s.text.includes("an sw-button answers i itself"),
+		"i-reopens-yk-button",
+		s.mode === "pinned" && s.text.includes("a yk-button answers i itself"),
 		s.text,
 	);
 
@@ -211,7 +211,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	s = await st();
 	check(
 		"pin-for-precedence",
-		s.mode === "pinned" && s.text.includes("an sw-button answers i itself"),
+		s.mode === "pinned" && s.text.includes("a yk-button answers i itself"),
 		s.text,
 	);
 	pt = await centerOf(`${COL}.querySelector('[data-mc-row="survey"]')`);
@@ -220,7 +220,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	s = await st();
 	check(
 		"hover-never-displaces-pin",
-		s.mode === "pinned" && s.text.includes("an sw-button answers i itself"),
+		s.mode === "pinned" && s.text.includes("a yk-button answers i itself"),
 		s.text,
 	);
 	await shot(cdp, "pin-outranks-hover.png");
@@ -270,8 +270,8 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	check("pointerdown-closes", s.mode === "closed", JSON.stringify(s.mode));
 
 	// --- edges: clamping and the flip ---
-	const EDGES = "document.querySelector('sw-gallery').shadowRoot.querySelector('swg-infopop-edges').shadowRoot";
-	pt = await centerOf(`${EDGES}.querySelectorAll('sw-button')[1]`); // far right
+	const EDGES = "document.querySelector('yk-gallery').shadowRoot.querySelector('ykg-infopop-edges').shadowRoot";
+	pt = await centerOf(`${EDGES}.querySelectorAll('yk-button')[1]`); // far right
 	await cdp.mouseMove(pt.x, pt.y);
 	await sleep(500);
 	s = await st();
@@ -290,7 +290,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// then hover it -- the popup must open ABOVE the anchor.
 	const targetH = await cdp.js(`
 	(() => {
-	  const el = ${EDGES}.querySelectorAll('sw-button')[0];
+	  const el = ${EDGES}.querySelectorAll('yk-button')[0];
 	  return Math.round(el.getBoundingClientRect().bottom + 20);
 	})()
 	`);
@@ -301,7 +301,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 		mobile: false,
 	});
 	await sleep(200);
-	pt = await centerOf(`${EDGES}.querySelectorAll('sw-button')[0]`); // far left
+	pt = await centerOf(`${EDGES}.querySelectorAll('yk-button')[0]`); // far left
 	await cdp.mouseMove(pt.x, pt.y);
 	await sleep(500);
 	s = await st();
diff --git a/packages/e2e/scenarios/lc-drive.ts b/packages/e2e/scenarios/lc-drive.ts
index 0168db7..58a65d7 100644
--- a/packages/e2e/scenarios/lc-drive.ts
+++ b/packages/e2e/scenarios/lc-drive.ts
@@ -1,5 +1,5 @@
-// Drives the sw-list gallery entry (the list region component composed into a
-// megacolumn console; sw-live-list under it) with TRUSTED input via CDP.
+// Drives the yk-list gallery entry (the list region component composed into a
+// megacolumn console; yk-live-list under it) with TRUSTED input via CDP.
 //
 // The demos churn on their own 4s timers; the scenario STOPS those timers up
 // front and drives data churn via JS at chosen moments -- data churn is
@@ -16,23 +16,23 @@
 // the pointer-leave release), the churn lockout (an Enter landing right after
 // a fallback move is DROPPED; flows again after the window), instant prepend
 // under key burst (p), focus memory across Tab-away, the drain-to-empty +
-// revive story, and the LIST NORMS, sw-list's own behavior: the header row
+// revive story, and the LIST NORMS, yk-list's own behavior: the header row
 // (a nav stop whose focus washes the whole list region) and the add-row
 // (a dives straight into text entry -- the direct marking on a text row;
-// Enter commits via the cancelable sw-list-add event; Escape abandons), and
+// Enter commits via the cancelable yk-list-add event; Escape abandons), and
 // the LIST-SCOPED KEYS: t (back to the list's header) and n (past the list),
-// claimed by the sw-list host element off the bubbling megacolumn-key dispatch
+// claimed by the yk-list host element off the bubbling megacolumn-key dispatch
 // and answered with megacolumn-focus-request; outside the list's rows the
-// letters are nobody's.  Structural row ids are name-prefixed by sw-list
+// letters are nobody's.  Structural row ids are name-prefixed by yk-list
 // (name="queue": queue-header, queue-add).
 
 import { type Scenario, type ScenarioContext, shot } from "yukey-testkit/checks";
 import { sleep } from "yukey-testkit/sleep";
 import { openGallery } from "../gallery.ts";
 
-// demo(i): the i-th swg-livecolumn-demo (0 = refill, 1 = drain).
+// demo(i): the i-th ykg-livecolumn-demo (0 = refill, 1 = drain).
 const demo = (i: number) =>
-	`document.querySelector('sw-gallery').shadowRoot.querySelectorAll('swg-livecolumn-demo')[${i}]`;
+	`document.querySelector('yk-gallery').shadowRoot.querySelectorAll('ykg-livecolumn-demo')[${i}]`;
 
 // Full state of one demo column: every rendered rowslot with its phase-visible
 // attributes, the machine-visible focus (ring classes + aria), and the log line.
@@ -55,18 +55,18 @@ const stateJs = (demoExpr: string) => `
     focusWithin: container.classList.contains('mc-focus-within'),
     emptyFocus: container.classList.contains('mc-empty-focus'),
     textMode: container.classList.contains('mc-text-mode'),
-    inputValue: root.querySelector('.sw-list-add [data-mc-input]')?.value ?? null,
-    inputFocused: root.activeElement === root.querySelector('.sw-list-add [data-mc-input]'),
+    inputValue: root.querySelector('.yk-list-add [data-mc-input]')?.value ?? null,
+    inputFocused: root.activeElement === root.querySelector('.yk-list-add [data-mc-input]'),
     // The header-focus region wash: a computed background on the group wrapper
     // (driven by the :has() rule in the demo's stylesheet, nothing script-side).
     groupWashed: (() => {
-      const g = root.querySelector('sw-list');
+      const g = root.querySelector('yk-list');
       if (g === null) return null;
       const bg = getComputedStyle(g).backgroundColor;
       return bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent';
     })(),
-    groupOutline: root.querySelector('sw-list') === null ? null
-      : getComputedStyle(root.querySelector('sw-list')).outlineStyle,
+    groupOutline: root.querySelector('yk-list') === null ? null
+      : getComputedStyle(root.querySelector('yk-list')).outlineStyle,
     rowFocused: root.querySelector('.mc-row-focused')?.dataset?.mcRow
       ?? root.querySelector('.mc-row-focused')?.closest('[data-mc-row]')?.dataset?.mcRow ?? null,
     auxFocused: root.querySelector('[data-mc-aux].mc-focused') !== null,
@@ -109,8 +109,8 @@ async function run(ctx: ScenarioContext): Promise<void> {
 
 	await openGallery(
 		ctx,
-		"#sw-list",
-		"document.readyState === 'complete' && document.querySelector('sw-gallery')?.shadowRoot?.querySelectorAll('swg-livecolumn-demo')?.length === 2",
+		"#yk-list",
+		"document.readyState === 'complete' && document.querySelector('yk-gallery')?.shadowRoot?.querySelectorAll('ykg-livecolumn-demo')?.length === 2",
 	);
 
 	// Determinism: stop both demos' own churn timers; the scenario churns by hand.
@@ -141,8 +141,8 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	const first = s.dataIds[0] as string;
 	// One down from the prepend row is the LIST HEADER ROW: a normal nav stop
 	// that speaks for the region.  While it holds focus the whole list washes
-	// (sw-list's own :has() rule); the focus treatment stays on the header
-	// itself -- the sw-list host draws no outline of its own.
+	// (yk-list's own :has() rule); the focus treatment stays on the header
+	// itself -- the yk-list host draws no outline of its own.
 	await cdp.key("ArrowDown");
 	s = await st();
 	check(
@@ -174,7 +174,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 
 	// Enter on the aux removes the task; the row ghosts in place, sheds its aux,
 	// STAYS focused (identity survives), and position snaps back to main.
-	// The row also FLASHES (sw-pressed) as instant acknowledgment: the fade to
+	// The row also FLASHES (yk-pressed) as instant acknowledgment: the fade to
 	// ghost alone was too quiet to confirm the keystroke landed.  The flash is
 	// 140ms, so a MutationObserver armed before the keystroke catches it.
 	await cdp.js(`
@@ -182,9 +182,9 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	    const root = ${demo(0)}.shadowRoot;
 	    window.__flashSeen = 0;
 	    window.__flashMo = new MutationObserver(muts => {
-	      for (const m of muts) if (m.target.classList.contains('sw-pressed')) window.__flashSeen += 1;
+	      for (const m of muts) if (m.target.classList.contains('yk-pressed')) window.__flashSeen += 1;
 	    });
-	    window.__flashMo.observe(root.querySelector('sw-live-list'),
+	    window.__flashMo.observe(root.querySelector('yk-live-list'),
 	      { attributes: true, attributeFilter: ['class'], subtree: true });
 	  })()
 	`);
@@ -377,7 +377,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// have shrunk the list away from where the ghost was).
 	const lbox = JSON.parse(
 		(await cdp.js(`
-	  (() => { const r = ${demo(0)}.shadowRoot.querySelector('sw-live-list').getBoundingClientRect();
+	  (() => { const r = ${demo(0)}.shadowRoot.querySelector('yk-live-list').getBoundingClientRect();
 	     return JSON.stringify({x: r.x + r.width/2, y: r.y + Math.max(r.height/2, 4)}); })()
 	`)) as string,
 	) as { x: number; y: number };
@@ -462,7 +462,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// old global hover pin story must hold: a hovered ghost defers past every
 	// timer, an insertion waits as a zero-height pending row, and pointer-leave
 	// releases both.
-	const LIST = `${demo(0)}.shadowRoot.querySelector('sw-live-list')`;
+	const LIST = `${demo(0)}.shadowRoot.querySelector('yk-live-list')`;
 	await cdp.js(`${LIST}.pointerSafety = 'freeze'`);
 	s = await st();
 	const fkey = s.dataIds[0] as string;
@@ -681,7 +681,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	check("committed-row-reachable-above-add-row", s1.rowFocused === newId, s1.rowFocused ?? "");
 
 	// --- phase 9: the list-scoped keys (t and n) -----------------------------
-	// The t/n norm: sw-list wires it itself (one megacolumn-key listener on
+	// The t/n norm: yk-list wires it itself (one megacolumn-key listener on
 	// the host element answering with megacolumn-focus-request), deliberately
 	// NOT gold markings.  Scope is DOM containment: within the list's rows t
 	// returns to the header and n jumps past the list; outside it the letters
@@ -745,27 +745,27 @@ async function run(ctx: ScenarioContext): Promise<void> {
 		JSON.stringify([s1.textMode, s1.inputValue]),
 	);
 
-	// --- phase 10: the sw-list component surface -----------------------------
+	// --- phase 10: the yk-list component surface -----------------------------
 	// Newly reachable since the list norms became a component (the
 	// list-component stage): the group identity for AT, and the cancelable
-	// commit (an owner that preventDefaults sw-list-add refuses the commit;
+	// commit (an owner that preventDefaults yk-list-add refuses the commit;
 	// the draft and text mode must survive so the user can fix the text).
 	// The demo's own handler never refuses, and a listener on the same element
 	// would run after it -- so the refusal is arranged upstream: a one-shot
 	// capture listener on the container cancels AND stops propagation (from
-	// sw-list's side that IS a refusing owner: sw-list-add came back
+	// yk-list's side that IS a refusing owner: yk-list-add came back
 	// defaultPrevented and nobody appended).
 	await cdp.js(`
 	  ${demo(1)}.shadowRoot.querySelector('[data-mc-container]')
-	    .addEventListener('sw-list-add', e => { e.preventDefault(); e.stopPropagation(); },
+	    .addEventListener('yk-list-add', e => { e.preventDefault(); e.stopPropagation(); },
 	                      { once: true, capture: true })
 	`);
 	const role = await cdp.js(`
-	  (() => { const l = ${demo(1)}.shadowRoot.querySelector('sw-list');
+	  (() => { const l = ${demo(1)}.shadowRoot.querySelector('yk-list');
 	     return JSON.stringify([l.getAttribute('role'), l.getAttribute('aria-label')]); })()
 	`);
 	check(
-		"sw-list-group-identity",
+		"yk-list-group-identity",
 		JSON.stringify(JSON.parse(role as string)) === JSON.stringify(["group", "task queue (live)"]),
 		String(role),
 	);
diff --git a/packages/e2e/scenarios/mc-drive.ts b/packages/e2e/scenarios/mc-drive.ts
index a0a7cb4..2dca2bc 100644
--- a/packages/e2e/scenarios/mc-drive.ts
+++ b/packages/e2e/scenarios/mc-drive.ts
@@ -10,8 +10,8 @@ import { openGallery } from "../gallery.ts";
 // real focus, its aria-activedescendant, ring positions, text mode, and the log line.
 const STATE_JS = `
 (() => {
-  const shell = document.querySelector("sw-gallery").shadowRoot;
-  const demos = [...shell.querySelectorAll("swg-megacolumn-demo")];
+  const shell = document.querySelector("yk-gallery").shadowRoot;
+  const demos = [...shell.querySelectorAll("ykg-megacolumn-demo")];
   const active = shell.activeElement;
   const out = { realFocus: null, columns: [] };
   for (const demo of demos) {
@@ -59,8 +59,8 @@ interface McState {
 // position, and the page scrollers that must NOT move.
 const SCROLL_JS = `
 (() => {
-  const shell = document.querySelector("sw-gallery").shadowRoot;
-  const demo = shell.querySelector("swg-megacolumn-scroll-demo");
+  const shell = document.querySelector("yk-gallery").shadowRoot;
+  const demo = shell.querySelector("ykg-megacolumn-scroll-demo");
   const sr = demo.shadowRoot;
   const box = sr.querySelector("[data-mc-container]");
   const focused = sr.querySelector(".mc-row-focused");
@@ -97,7 +97,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	await openGallery(
 		ctx,
 		"#megacolumn",
-		"document.readyState === 'complete' && !!document.querySelector('sw-gallery')?.shadowRoot?.querySelector('swg-megacolumn-demo')",
+		"document.readyState === 'complete' && !!document.querySelector('yk-gallery')?.shadowRoot?.querySelector('ykg-megacolumn-demo')",
 	);
 
 	// The gallery seats initial focus in the entry's first tab stop on open:
@@ -129,7 +129,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// of 12 = "six nav links" broke when the gallery grew); count the nav links
 	// live and add the demo's own stops with margin.
 	const navLinks = (await cdp.js(
-		"document.querySelector('sw-gallery').shadowRoot.querySelectorAll('nav a').length",
+		"document.querySelector('yk-gallery').shadowRoot.querySelectorAll('nav a').length",
 	)) as number;
 	for (let i = 0; i < navLinks + 12; i++) {
 		await cdp.key("Tab");
@@ -323,8 +323,8 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// megacolumn-key.  A tap produces a click, so this is also the touch story.
 	const portCenter = async (selector: string): Promise<{ x: number; y: number }> => {
 		const v = await cdp.js(`
-		(() => { const el = document.querySelector("sw-gallery").shadowRoot
-		     .querySelector("swg-megacolumn-demo").shadowRoot.querySelector('${selector}');
+		(() => { const el = document.querySelector("yk-gallery").shadowRoot
+		     .querySelector("ykg-megacolumn-demo").shadowRoot.querySelector('${selector}');
 		     el.scrollIntoView({block: "nearest", behavior: "instant"});
 		     const r = el.getBoundingClientRect();
 		     return JSON.stringify({x: r.x + r.width/2, y: r.y + r.height/2}); })()
diff --git a/packages/e2e/scenarios/yk-button-drive.ts b/packages/e2e/scenarios/yk-button-drive.ts
index c7c85fe..003030a 100644
--- a/packages/e2e/scenarios/yk-button-drive.ts
+++ b/packages/e2e/scenarios/yk-button-drive.ts
@@ -1,9 +1,9 @@
-// Drives the sw-button gallery entry with TRUSTED key events via CDP,
+// Drives the yk-button gallery entry with TRUSTED key events via CDP,
 // reading state through the shadow roots between steps.  Covers: standalone
 // tab traversal + native activation + focused hotkey/i, the in-column claims
 // (Enter/Space, column-scoped hotkeys, x/Delete on aux, i, disabled
 // claim-and-decline), the two-column scoping proof, and activation feedback
-// (the sw-held / sw-pressed classes; the 120ms flash is caught with a
+// (the yk-held / yk-pressed classes; the 120ms flash is caught with a
 // MutationObserver installed before the keystroke, not with a screenshot race).
 
 import { type Scenario, type ScenarioContext, shot } from "yukey-testkit/checks";
@@ -14,10 +14,10 @@ import { openGallery } from "../gallery.ts";
 // each demo's log line, and the columns' focused rows.
 const STATE_JS = `
 (() => {
-  const shell = document.querySelector("sw-gallery").shadowRoot;
+  const shell = document.querySelector("yk-gallery").shadowRoot;
   const out = { realFocus: null, standalone: null, columns: [] };
   const active = shell.activeElement;
-  const sa = shell.querySelector("swg-button-standalone");
+  const sa = shell.querySelector("ykg-button-standalone");
   if (sa) {
     const sr = sa.shadowRoot;
     out.standalone = {
@@ -27,7 +27,7 @@ const STATE_JS = `
     };
     if (sa === active) out.realFocus = "standalone/" + (sr.activeElement?.label ?? "?");
   }
-  for (const col of shell.querySelectorAll("swg-button-column")) {
+  for (const col of shell.querySelectorAll("ykg-button-column")) {
     const sr = col.shadowRoot;
     const container = sr.querySelector("[data-mc-container]");
     const label = col.getAttribute("label");
@@ -45,7 +45,7 @@ const STATE_JS = `
 })()
 `;
 
-interface SwColumn {
+interface YkColumn {
 	label: string;
 	rowFocused: string | null;
 	focusWithin: boolean;
@@ -54,19 +54,19 @@ interface SwColumn {
 	cargoRows: number;
 }
 
-interface SwState {
+interface YkState {
 	realFocus: string | null;
 	standalone: { log: string; logIsInfo: boolean; focusedLabel: string | null };
-	columns: SwColumn[];
+	columns: YkColumn[];
 }
 
 const READY_JS =
-	"document.readyState === 'complete' && !!document.querySelector('sw-gallery')?.shadowRoot?.querySelector('swg-button-standalone')";
+	"document.readyState === 'complete' && !!document.querySelector('yk-gallery')?.shadowRoot?.querySelector('ykg-button-standalone')";
 
 async function run(ctx: ScenarioContext): Promise<void> {
 	const { cdp, checks } = ctx;
 	const check = (name: string, cond: boolean, detail = "") => checks.check(name, cond, detail);
-	const st = async (): Promise<SwState> => JSON.parse((await cdp.js(STATE_JS)) as string);
+	const st = async (): Promise<YkState> => JSON.parse((await cdp.js(STATE_JS)) as string);
 	const centerOf = async (jsElExpr: string): Promise<{ x: number; y: number }> => {
 		const box = await cdp.js(`
 		(() => {
@@ -77,11 +77,11 @@ async function run(ctx: ScenarioContext): Promise<void> {
 		return JSON.parse(box as string);
 	};
 
-	await openGallery(ctx, "#sw-button", READY_JS);
+	await openGallery(ctx, "#yk-button", READY_JS);
 
 	// --- standalone variant ---
 	// The gallery seats initial focus in the entry's first tab stop on open:
-	// for #sw-button that is the first standalone button.  (Seating happens a
+	// for #yk-button that is the first standalone button.  (Seating happens a
 	// frame after render; poll briefly.)
 	let landed = false;
 	let s = await st();
@@ -156,9 +156,9 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	);
 
 	// Mouse click on the dock button (trusted click via CDP).
-	const SA_ROOT = "document.querySelector(\"sw-gallery\").shadowRoot.querySelector(\"swg-button-standalone\")";
+	const SA_ROOT = "document.querySelector(\"yk-gallery\").shadowRoot.querySelector(\"ykg-button-standalone\")";
 	let pt = await centerOf(
-		`[...${SA_ROOT}.shadowRoot.querySelectorAll("sw-button")].find(x => x.label === "Dock at station")`,
+		`[...${SA_ROOT}.shadowRoot.querySelectorAll("yk-button")].find(x => x.label === "Dock at station")`,
 	);
 	await cdp.click(pt.x, pt.y);
 	s = await st();
@@ -177,7 +177,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 		}
 	}
 	check("tab-into-ops-console", landed, JSON.stringify(s.realFocus));
-	const ops = (v: SwState): SwColumn => v.columns[0] as SwColumn;
+	const ops = (v: YkState): YkColumn => v.columns[0] as YkColumn;
 	check("ops-first-row-dock", ops(s).rowFocused === "dock", JSON.stringify(ops(s)));
 
 	// Enter on the focused button row.
@@ -221,7 +221,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	await cdp.key("ArrowDown");
 	s = await st();
 	check("disabled-row-focusable", ops(s).rowFocused === "purge", JSON.stringify(ops(s)));
-	const MAIN_SCROLL = "document.querySelector('sw-gallery').shadowRoot.querySelector('main').scrollTop";
+	const MAIN_SCROLL = "document.querySelector('yk-gallery').shadowRoot.querySelector('main').scrollTop";
 	const before = await cdp.js(MAIN_SCROLL);
 	await cdp.key(" ");
 	s = await st();
@@ -269,13 +269,13 @@ async function run(ctx: ScenarioContext): Promise<void> {
 		JSON.stringify(ops(s)),
 	);
 
-	// A trusted mouse click on a row-mode sw-button: exactly ONE activation.
+	// A trusted mouse click on a row-mode yk-button: exactly ONE activation.
 	// The controller synthesizes Enter from unclaimed row clicks (pointer
-	// activation for plain rows); sw-button claims its own clicks so the
+	// activation for plain rows); yk-button claims its own clicks so the
 	// synthesis skips it -- a broken dedup would log two activations here.
-	const OPS_BEACON = `document.querySelector("sw-gallery").shadowRoot
-	    .querySelector("swg-button-column").shadowRoot
-	    .querySelector('[data-mc-row="beacon"] sw-button')`;
+	const OPS_BEACON = `document.querySelector("yk-gallery").shadowRoot
+	    .querySelector("ykg-button-column").shadowRoot
+	    .querySelector('[data-mc-row="beacon"] yk-button')`;
 	await cdp.js(
 		`window.__acts = 0; (${OPS_BEACON}).addEventListener('click', () => { window.__acts += 1 })`,
 	);
@@ -290,8 +290,8 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	await cdp.key("Tab");
 	s = await st();
 	check("tab-into-port", s.realFocus === "port console/container", JSON.stringify(s.realFocus));
-	const port = (v: SwState): SwColumn => v.columns[1] as SwColumn;
-	const starboard = (v: SwState): SwColumn => v.columns[2] as SwColumn;
+	const port = (v: YkState): YkColumn => v.columns[1] as YkColumn;
+	const starboard = (v: YkState): YkColumn => v.columns[2] as YkColumn;
 	// The focused row's OWN hotkey does nothing: not a jump (already here),
 	// not an activation (double-tap must not out-power single-tap).
 	await cdp.key("d"); // focus sits on port's dock row
@@ -373,16 +373,16 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	check("scroll-memory-not-on-dock", ops(s).rowFocused !== "dock", JSON.stringify(ops(s)));
 	const DOCK_VIS = `
 	(() => {
-	  const m = document.querySelector('sw-gallery').shadowRoot.querySelector('main');
-	  const row = document.querySelector('sw-gallery').shadowRoot
-	    .querySelector('swg-button-column').shadowRoot
+	  const m = document.querySelector('yk-gallery').shadowRoot.querySelector('main');
+	  const row = document.querySelector('yk-gallery').shadowRoot
+	    .querySelector('ykg-button-column').shadowRoot
 	    .querySelector('[data-mc-row="dock"]');
 	  const mr = m.getBoundingClientRect(), rr = row.getBoundingClientRect();
 	  return JSON.stringify({scrollTop: m.scrollTop,
 	    visible: rr.bottom > mr.top && rr.top < mr.bottom});
 	})()`;
 	await cdp.js(
-		"const m = document.querySelector('sw-gallery').shadowRoot.querySelector('main'); m.scrollTop = m.scrollHeight",
+		"const m = document.querySelector('yk-gallery').shadowRoot.querySelector('main'); m.scrollTop = m.scrollHeight",
 	);
 	const visBefore = JSON.parse((await cdp.js(DOCK_VIS)) as string) as {
 		scrollTop: number;
@@ -404,7 +404,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	await shot(cdp, "hotkey-jump.png");
 	await cdp.cmd("Emulation.clearDeviceMetricsOverride");
 
-	// --- activation feedback: the sw-held / sw-pressed classes ---
+	// --- activation feedback: the yk-held / yk-pressed classes ---
 	// Reload to reset the demos; initial focus re-seats on the first standalone button.
 	await cdp.cmd("Page.reload", { ignoreCache: true });
 	for (let i = 0; i < 50; i++) {
@@ -421,14 +421,14 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// keystroke, so a 120ms flash cannot slip between CDP roundtrips.
 	// Each arm disconnects the previous observer: stale observers otherwise
 	// increment whatever __flash is current when their callbacks run.
-	// find: a JS expression yielding the sw-button host to watch.
+	// find: a JS expression yielding the yk-button host to watch.
 	const armFlash = async (find: string): Promise<void> => {
 		await cdp.js(`
 		(() => {
 		  window.__flash?.obs.disconnect();
 		  const b = ${find};
 		  const obs = new MutationObserver(() => {
-		    if (b.classList.contains("sw-pressed")) window.__flash.count += 1;
+		    if (b.classList.contains("yk-pressed")) window.__flash.count += 1;
 		  });
 		  obs.observe(b, { attributes: true, attributeFilter: ["class"] });
 		  window.__flash = { el: b, count: 0, obs };
@@ -444,7 +444,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	};
 	const flashCleared = async (): Promise<boolean> => {
 		for (let i = 0; i < 30; i++) {
-			if ((await cdp.js("!window.__flash.el.classList.contains('sw-pressed')")) === true) {
+			if ((await cdp.js("!window.__flash.el.classList.contains('yk-pressed')")) === true) {
 				return true;
 			}
 			await sleep(20);
@@ -453,9 +453,9 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	};
 
 	const SA_BUTTON = (label: string) =>
-		`[...document.querySelector("sw-gallery").shadowRoot
-		    .querySelector("swg-button-standalone").shadowRoot
-		    .querySelectorAll("sw-button")].find(x => x.label === "${label}")`;
+		`[...document.querySelector("yk-gallery").shadowRoot
+		    .querySelector("ykg-button-standalone").shadowRoot
+		    .querySelectorAll("yk-button")].find(x => x.label === "${label}")`;
 
 	// Keyboard activation flashes: trusted Enter on the focused standalone button.
 	await armFlash(SA_BUTTON("Dock at station"));
@@ -473,19 +473,19 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	// Held under a trusted pointer: press without release, look, release.
 	pt = await centerOf(SA_BUTTON("Dock at station"));
 	await cdp.mouseDown(pt.x, pt.y);
-	const held = await cdp.js(`(${SA_BUTTON("Dock at station")}).classList.contains('sw-held')`);
+	const held = await cdp.js(`(${SA_BUTTON("Dock at station")}).classList.contains('yk-held')`);
 	check("held-while-pointer-down", held === true);
 	// The held look IS the flash look, and it sits still: the one screenshot
 	// a 120ms transient can't reliably give us.
 	await shot(cdp, "press-flash.png");
 	await cdp.mouseUp(pt.x, pt.y);
-	const released = await cdp.js(`!(${SA_BUTTON("Dock at station")}).classList.contains('sw-held')`);
+	const released = await cdp.js(`!(${SA_BUTTON("Dock at station")}).classList.contains('yk-held')`);
 	check("held-releases-on-pointer-up", released === true);
 	// The release click flashes Dock; let that settle before the disabled check
 	// so no mutation from it is in flight when the observer moves.
 	for (let i = 0; i < 30; i++) {
 		if (
-			(await cdp.js(`(${SA_BUTTON("Dock at station")}).classList.contains('sw-pressed')`)) !== true
+			(await cdp.js(`(${SA_BUTTON("Dock at station")}).classList.contains('yk-pressed')`)) !== true
 		) {
 			break;
 		}
@@ -497,7 +497,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	await armFlash(SA_BUTTON("self destruct"));
 	await cdp.mouseDown(pt.x, pt.y);
 	const heldDisabled = await cdp.js(
-		`(${SA_BUTTON("self destruct")}).classList.contains('sw-held')`,
+		`(${SA_BUTTON("self destruct")}).classList.contains('yk-held')`,
 	);
 	await cdp.mouseUp(pt.x, pt.y);
 	check("disabled-never-held", heldDisabled !== true);
@@ -519,9 +519,9 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	}
 	check("feedback-tab-into-ops", landed, JSON.stringify(s.realFocus));
 	const COL_BUTTON = (row: string) =>
-		`document.querySelector("sw-gallery").shadowRoot
-		    .querySelector("swg-button-column").shadowRoot
-		    .querySelector('[data-mc-row="${row}"] sw-button')`;
+		`document.querySelector("yk-gallery").shadowRoot
+		    .querySelector("ykg-button-column").shadowRoot
+		    .querySelector('[data-mc-row="${row}"] yk-button')`;
 	await armFlash(COL_BUTTON("dock"));
 	await cdp.key("Enter");
 	check("row-flash-on-trusted-enter", await flashSeen());
@@ -541,7 +541,7 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	for (let i = 0; i < 50; i++) {
 		if (
 			(await cdp.js(
-				"document.readyState === 'complete' && !!document.querySelector('sw-gallery')?.shadowRoot?.querySelector('swg-megacolumn-demo')",
+				"document.readyState === 'complete' && !!document.querySelector('yk-gallery')?.shadowRoot?.querySelector('ykg-megacolumn-demo')",
 			)) === true
 		) {
 			break;
@@ -551,8 +551,8 @@ async function run(ctx: ScenarioContext): Promise<void> {
 	landed = false;
 	for (let i = 0; i < 20; i++) {
 		if (
-			(await cdp.js(`document.querySelector("sw-gallery").shadowRoot
-				.querySelector("swg-megacolumn-demo").shadowRoot
+			(await cdp.js(`document.querySelector("yk-gallery").shadowRoot
+				.querySelector("ykg-megacolumn-demo").shadowRoot
 				.querySelector("[data-mc-container]").classList.contains("mc-focus-within")`)) === true
 		) {
 			landed = true;
@@ -561,12 +561,12 @@ async function run(ctx: ScenarioContext): Promise<void> {
 		await sleep(50);
 	}
 	check("plain-rows-focus-seated", landed);
-	await armFlash(`document.querySelector("sw-gallery").shadowRoot
-	    .querySelector("swg-megacolumn-demo").shadowRoot
+	await armFlash(`document.querySelector("yk-gallery").shadowRoot
+	    .querySelector("ykg-megacolumn-demo").shadowRoot
 	    .querySelector('[data-mc-row="beacon"]')`);
 	await cdp.key("Enter");
 	check("plain-row-flash-on-trusted-enter", await flashSeen());
 	check("plain-row-flash-clears", await flashCleared());
 }
 
-export const swDrive: Scenario = { name: "sw", run };
+export const ykDrive: Scenario = { name: "yk", run };
diff --git a/packages/yukey/README.md b/packages/yukey/README.md
index 3655aab..d211c2f 100644
--- a/packages/yukey/README.md
+++ b/packages/yukey/README.md
@@ -24,14 +24,14 @@ Use
 Every module is importable by its file name:
 
 ```ts
-import "yukey/sw-button";                                  // registers the custom element
+import "yukey/yk-button";                                  // registers the custom element
 import { MegacolumnController } from "yukey/megacolumn-controller";
 import { installTokens } from "yukey/tokens";
 
 installTokens();                                           // once, at startup: the design tokens into :root
 ```
 
-Components (`sw-button`, `sw-list`, `sw-live-list`, `sw-infopop`) register themselves on import.
+Components (`yk-button`, `yk-list`, `yk-live-list`, `yk-infopop`) register themselves on import.
 Mechanisms (`megacolumn-controller`, `tab-cycle`, `focus-stack`, `press-flash`, and so on) are plain exports.
 The gallery (`yukey/gallery/main`) is the living documentation: every component, every variant, driven by the real thing.
 
diff --git a/packages/yukey/src/adopt-into-root.ts b/packages/yukey/src/adopt-into-root.ts
index 2ad1ec9..9955dfb 100644
--- a/packages/yukey/src/adopt-into-root.ts
+++ b/packages/yukey/src/adopt-into-root.ts
@@ -1,5 +1,5 @@
 // Stylesheet adoption for components that render into their OWNER's tree.
-// Megacolumn-composed components (sw-live-list mega mode, sw-list) render light-DOM
+// Megacolumn-composed components (yk-live-list mega mode, yk-list) render light-DOM
 // rows -- the megacolumn scan and aria-activedescendant cannot cross shadow
 // boundaries -- so their styles must reach the owner's root, not a shadow root of
 // their own.  This appends a sheet to the root's adopted stylesheets, once per root.
@@ -8,7 +8,7 @@
 // belongs to the owner.  Where constructed stylesheets are unsupported (jsdom),
 // fall back to one marked <style> tag -- the same fallback shape lit itself uses;
 // `marker` is the attribute name that makes the fallback idempotent (one per
-// adopter class, e.g. "data-sw-live-list").
+// adopter class, e.g. "data-yk-live-list").
 
 import type { CSSResult } from "lit";
 
diff --git a/packages/yukey/src/focus-stack.ts b/packages/yukey/src/focus-stack.ts
index e768222..c31dde2 100644
--- a/packages/yukey/src/focus-stack.ts
+++ b/packages/yukey/src/focus-stack.ts
@@ -99,7 +99,7 @@ export class FocusStack {
 	}
 
 	// The end of the line for pops: the page shell's one predictable focus home
-	// (the root surface -- sw-app points this at its map instance).
+	// (the root surface -- yk-app points this at its map instance).
 	setFallback(fallback: () => HTMLElement | null): void {
 		this.#fallback = fallback;
 	}
diff --git a/packages/yukey/src/gallery/entry.ts b/packages/yukey/src/gallery/entry.ts
index 9933ab1..bcfa1db 100644
--- a/packages/yukey/src/gallery/entry.ts
+++ b/packages/yukey/src/gallery/entry.ts
@@ -24,7 +24,7 @@ export interface GalleryVariant {
 }
 
 export interface GalleryEntry {
-	// Hash-route slug; conventionally the element tag name (e.g. "sw-live-list").
+	// Hash-route slug; conventionally the element tag name (e.g. "yk-live-list").
 	id: string;
 	title: string;
 	// What the component is for, in a sentence or two.  This is living documentation:
diff --git a/packages/yukey/src/gallery/main.ts b/packages/yukey/src/gallery/main.ts
index 8c6a61e..226ce27 100644
--- a/packages/yukey/src/gallery/main.ts
+++ b/packages/yukey/src/gallery/main.ts
@@ -1,5 +1,5 @@
 // Gallery entry point (esbuild bundles this to static/gallery.js; gallery.html hosts it).
-// Registering <sw-gallery> is all that's needed; it pulls the entry registry in turn.
+// Registering <yk-gallery> is all that's needed; it pulls the entry registry in turn.
 import { installTokens } from "../tokens.js";
 import "./yk-gallery.js";
 
diff --git a/packages/yukey/src/gallery/registry.ts b/packages/yukey/src/gallery/registry.ts
index 07d1fab..338c97c 100644
--- a/packages/yukey/src/gallery/registry.ts
+++ b/packages/yukey/src/gallery/registry.ts
@@ -4,17 +4,17 @@
 
 import megacolumn from "../megacolumn.gallery.js";
 import tokens from "../tokens.gallery.js";
-import swButton from "../yk-button.gallery.js";
-import swInfopop from "../yk-infopop.gallery.js";
-import swList from "../yk-list.gallery.js";
-import swLiveList from "../yk-live-list.gallery.js";
+import ykButton from "../yk-button.gallery.js";
+import ykInfopop from "../yk-infopop.gallery.js";
+import ykList from "../yk-list.gallery.js";
+import ykLiveList from "../yk-live-list.gallery.js";
 import type { GalleryEntry } from "./entry.js";
 
 export const entries: GalleryEntry[] = [
 	megacolumn,
-	swButton,
-	swInfopop,
-	swList,
-	swLiveList,
+	ykButton,
+	ykInfopop,
+	ykList,
+	ykLiveList,
 	tokens,
 ];
diff --git a/packages/yukey/src/gallery/yk-gallery.ts b/packages/yukey/src/gallery/yk-gallery.ts
index 16c5d9e..8be2534 100644
--- a/packages/yukey/src/gallery/yk-gallery.ts
+++ b/packages/yukey/src/gallery/yk-gallery.ts
@@ -1,5 +1,5 @@
 // The component gallery shell: nav of registered entries on the left, the selected
-// entry's variants on the right.  Hash routing (#sw-live-list) so entries are
+// entry's variants on the right.  Hash routing (#yk-live-list) so entries are
 // linkable and reload survives.  Styled entirely from the design tokens: the shell
 // is the token vocabulary's first real consumer, and doubles as a proof of it.
 
@@ -10,82 +10,82 @@ import { deepTabbables, installTabContainment } from "../tab-cycle.js";
 import type { GalleryEntry } from "./entry.js";
 import { entries } from "./registry.js";
 
-@customElement("sw-gallery")
-export class SwGallery extends LitElement {
+@customElement("yk-gallery")
+export class YkGallery extends LitElement {
 	static styles = css`
 		:host {
 			display: grid;
 			grid-template-columns: 220px 1fr;
 			height: 100%;
-			font: var(--sw-type-body) var(--sw-font);
-			color: var(--sw-text);
-			background: var(--sw-bg-void);
+			font: var(--yk-type-body) var(--yk-font);
+			color: var(--yk-text);
+			background: var(--yk-bg-void);
 		}
 		nav {
-			border-right: 1px solid var(--sw-border-dim);
-			padding: var(--sw-space-md) 0;
+			border-right: 1px solid var(--yk-border-dim);
+			padding: var(--yk-space-md) 0;
 			overflow-y: auto;
 		}
 		nav .head {
-			padding: 0 var(--sw-space-lg) var(--sw-space-md);
-			color: var(--sw-text-dim);
-			letter-spacing: var(--sw-tracking-wide);
+			padding: 0 var(--yk-space-lg) var(--yk-space-md);
+			color: var(--yk-text-dim);
+			letter-spacing: var(--yk-tracking-wide);
 			text-transform: uppercase;
-			font-size: var(--sw-type-small);
+			font-size: var(--yk-type-small);
 		}
 		nav a {
 			display: block;
-			min-height: var(--sw-hit-min-h);
+			min-height: var(--yk-hit-min-h);
 			box-sizing: border-box;
-			padding: var(--sw-space-xs) var(--sw-space-lg);
-			color: var(--sw-accent-bright);
+			padding: var(--yk-space-xs) var(--yk-space-lg);
+			color: var(--yk-accent-bright);
 			text-decoration: none;
 		}
 		nav a:hover {
-			color: var(--sw-text-bright);
-			background: var(--sw-wash-hover);
+			color: var(--yk-text-bright);
+			background: var(--yk-wash-hover);
 		}
 		nav a.current {
-			color: var(--sw-text-bright);
-			background: var(--sw-wash-selected);
+			color: var(--yk-text-bright);
+			background: var(--yk-wash-selected);
 		}
 		nav a:focus-visible {
-			outline: var(--sw-focus-ring);
-			outline-offset: calc(-1 * var(--sw-focus-ring-width));
+			outline: var(--yk-focus-ring);
+			outline-offset: calc(-1 * var(--yk-focus-ring-width));
 		}
 		main {
-			padding: var(--sw-space-xl) 28px;
+			padding: var(--yk-space-xl) 28px;
 			overflow-y: auto;
 		}
 		main h1 {
-			font-size: var(--sw-type-head);
-			color: var(--sw-text-bright);
-			letter-spacing: var(--sw-tracking);
-			margin: 0 0 var(--sw-space-sm);
+			font-size: var(--yk-type-head);
+			color: var(--yk-text-bright);
+			letter-spacing: var(--yk-tracking);
+			margin: 0 0 var(--yk-space-sm);
 		}
 		.description {
 			max-width: 70ch;
-			color: var(--sw-text-hi);
-			margin-bottom: var(--sw-space-xl);
+			color: var(--yk-text-hi);
+			margin-bottom: var(--yk-space-xl);
 		}
 		.variant {
-			margin-bottom: var(--sw-space-xxl);
+			margin-bottom: var(--yk-space-xxl);
 		}
 		.variant .caption {
 			max-width: 70ch;
-			color: var(--sw-text-dim);
-			margin-bottom: var(--sw-space-sm);
+			color: var(--yk-text-dim);
+			margin-bottom: var(--yk-space-sm);
 		}
 		.variant .stage {
-			border: 1px solid var(--sw-border-dim);
-			padding: var(--sw-space-lg);
+			border: 1px solid var(--yk-border-dim);
+			padding: var(--yk-space-lg);
 			max-width: 560px;
 		}
 		.variant .stage.wide {
 			max-width: none;
 		}
 		.quiet {
-			color: var(--sw-text-faint);
+			color: var(--yk-text-faint);
 		}
 	`;
 
@@ -126,7 +126,7 @@ export class SwGallery extends LitElement {
 		const current = entries.find((e) => e.id === this.currentId);
 		return html`
 			<nav>
-				<div class="head">starweave gallery</div>
+				<div class="head">yukey gallery</div>
 				${
 			entries.map(
 				(e) =>
@@ -137,7 +137,7 @@ export class SwGallery extends LitElement {
 		}
 			</nav>
 			<main>${current ? this.renderEntry(current) : this.renderIndex()}</main>
-			<sw-infopop></sw-infopop>
+			<yk-infopop></yk-infopop>
 		`;
 	}
 
@@ -161,7 +161,7 @@ export class SwGallery extends LitElement {
 		return html`
 			<h1>component gallery</h1>
 			<div class="description">
-				Living documentation and dev harness for the starweave component library.
+				Living documentation and dev harness for yukey.
 				Pick a component from the nav.  To add one: write a
 				<span class="quiet">foo.gallery.ts</span> beside the component and register it in
 				<span class="quiet">gallery/registry.ts</span>.
diff --git a/packages/yukey/src/infopop-place.ts b/packages/yukey/src/infopop-place.ts
index 8170eb9..f8b3880 100644
--- a/packages/yukey/src/infopop-place.ts
+++ b/packages/yukey/src/infopop-place.ts
@@ -1,6 +1,6 @@
 // The infopop placement rule: pure geometry, no DOM types, no measurement.
 // Given the anchor's rectangle, the popup's size, and the viewport size, decide
-// where the popup goes.  sw-infopop.ts feeds it measured values and applies the result.
+// where the popup goes.  yk-infopop.ts feeds it measured values and applies the result.
 //
 // The rule (deterministic on purpose; simple beats clever for a transient surface):
 //
diff --git a/packages/yukey/src/megacolumn-controller.test.ts b/packages/yukey/src/megacolumn-controller.test.ts
index 3edf71b..5c6a6b0 100644
--- a/packages/yukey/src/megacolumn-controller.test.ts
+++ b/packages/yukey/src/megacolumn-controller.test.ts
@@ -13,7 +13,7 @@ import {
 	MegacolumnController,
 	type MegacolumnKeyDetail,
 } from "./megacolumn-controller.js";
-import type { SwInfoDetail } from "./yk-info.js";
+import type { YkInfoDetail } from "./yk-info.js";
 
 // jsdom has no layout and implements no scrollIntoView; the controller calls it
 // on every focus-identity change (logical focus must never sit off-screen).
@@ -479,7 +479,7 @@ describe("pointer activation (click synthesizes Enter)", () => {
 		// The lockout protects keystrokes already in flight when focus moved under
 		// them; a click carries its target in its coordinates and its mousedown just
 		// resolved the aim.  (Moving-target clicks are the geometry owner's problem:
-		// sw-live-list's guard stops them in the capture phase before this synthesis.)
+		// yk-live-list's guard stops them in the capture phase before this synthesis.)
 		const { root, render, ctl, key } = makeRig(threeRows);
 		key("ArrowDown"); // -> b
 		render(`<div data-mc-row="a">alpha</div><div data-mc-row="c">gamma</div>`); // b vanishes
@@ -1246,7 +1246,7 @@ describe("column-scoped hotkey markings (data-mc-hotkey)", () => {
 			<div data-mc-row="atlas" data-mc-hotkey="i"><span data-mc-main>atlas</span></div>
 		`);
 		let infos = 0;
-		container.addEventListener("sw-info", () => {
+		container.addEventListener("yk-info", () => {
 			infos += 1;
 		});
 		key("i");
@@ -1263,7 +1263,7 @@ describe("column-scoped hotkey markings (data-mc-hotkey)", () => {
 			<div data-mc-row="atlas" data-mc-hotkey="i" data-mc-hotkey-direct><span data-mc-main>atlas</span></div>
 		`);
 		let infos = 0;
-		container.addEventListener("sw-info", () => {
+		container.addEventListener("yk-info", () => {
 			infos += 1;
 		});
 		key("i"); // nobody claims either dispatch
@@ -1310,13 +1310,13 @@ describe("column-scoped hotkey markings (data-mc-hotkey)", () => {
 	});
 });
 describe("the i fallback for [data-info] carriers", () => {
-	it("answers an unclaimed i with sw-info from the row's data-info", () => {
+	it("answers an unclaimed i with yk-info from the row's data-info", () => {
 		const { root, container, key } = makeRig(
 			`<div data-mc-row="a" data-info="alpha is a placeholder row">alpha</div>`,
 		);
-		const got: SwInfoDetail[] = [];
-		container.addEventListener("sw-info", (e) => {
-			got.push((e as CustomEvent<SwInfoDetail>).detail);
+		const got: YkInfoDetail[] = [];
+		container.addEventListener("yk-info", (e) => {
+			got.push((e as CustomEvent<YkInfoDetail>).detail);
 		});
 		const e = key("i");
 		expect(got).toEqual([
@@ -1333,7 +1333,7 @@ describe("the i fallback for [data-info] carriers", () => {
 			if ((e as CustomEvent<MegacolumnKeyDetail>).detail.key === "i") e.preventDefault();
 		});
 		let infos = 0;
-		container.addEventListener("sw-info", () => {
+		container.addEventListener("yk-info", () => {
 			infos += 1;
 		});
 		key("i");
@@ -1347,9 +1347,9 @@ describe("the i fallback for [data-info] carriers", () => {
 				<span data-mc-aux data-info="the x's story">x</span>
 			</div>
 		`);
-		const got: SwInfoDetail[] = [];
-		container.addEventListener("sw-info", (e) => {
-			got.push((e as CustomEvent<SwInfoDetail>).detail);
+		const got: YkInfoDetail[] = [];
+		container.addEventListener("yk-info", (e) => {
+			got.push((e as CustomEvent<YkInfoDetail>).detail);
 		});
 		key("ArrowRight"); // onto the aux control
 		key("i");
@@ -1363,8 +1363,8 @@ describe("the i fallback for [data-info] carriers", () => {
 	it("shifted I answers too", () => {
 		const { container, key } = makeRig(`<div data-mc-row="a" data-info="story">alpha</div>`);
 		const got: string[] = [];
-		container.addEventListener("sw-info", (e) => {
-			got.push((e as CustomEvent<SwInfoDetail>).detail.content);
+		container.addEventListener("yk-info", (e) => {
+			got.push((e as CustomEvent<YkInfoDetail>).detail.content);
 		});
 		key("I", { shiftKey: true });
 		expect(got).toEqual(["story"]);
@@ -1373,7 +1373,7 @@ describe("the i fallback for [data-info] carriers", () => {
 	it("i on a row without data-info stays unconsumed and dispatches nothing", () => {
 		const { container, key } = makeRig(threeRows);
 		let infos = 0;
-		container.addEventListener("sw-info", () => {
+		container.addEventListener("yk-info", () => {
 			infos += 1;
 		});
 		const e = key("i");
@@ -1387,7 +1387,7 @@ describe("the i fallback for [data-info] carriers", () => {
 		const { container, key } = makeRig(threeRows);
 		container.dataset.info = "the container's own story";
 		let infos = 0;
-		container.addEventListener("sw-info", () => {
+		container.addEventListener("yk-info", () => {
 			infos += 1;
 		});
 		const e = key("i");
diff --git a/packages/yukey/src/megacolumn-controller.ts b/packages/yukey/src/megacolumn-controller.ts
index 93d1785..2d1a2d5 100644
--- a/packages/yukey/src/megacolumn-controller.ts
+++ b/packages/yukey/src/megacolumn-controller.ts
@@ -63,17 +63,17 @@
 //                              is cheap to cancel, so just give the order; a text dive is
 //                              undone by Escape, nothing committed).  If reaching
 //                              for this makes you think twice, that is the design working.
-//   [data-info="<content>"]    the info norm's carrier attribute (SSOT: sw-info.ts).
+//   [data-info="<content>"]    the info norm's carrier attribute (SSOT: yk-info.ts).
 //                              When a forwarded i comes back unclaimed, the container
 //                              answers for the nearest carrier from the focused control
-//                              up to the row (inclusive), dispatching sw-info itself --
+//                              up to the row (inclusive), dispatching yk-info itself --
 //                              so plain rows get the i norm with no widget code.
 //
 // Same-shadow-tree note: aria-activedescendant can only reference an id in the same
 // tree as the container, and this binding enforces that shape by construction --
 // rows are found with querySelectorAll under the container, which cannot cross
 // shadow boundaries or reach slotted light-DOM content.  Render rows in the host's
-// own shadow root (render-props style, as sw-live-list does); slotting rows in from
+// own shadow root (render-props style, as yk-live-list does); slotting rows in from
 // outside is not supported and scans as an empty column.
 //
 // Keys the machine forwards are delivered as a cancelable `megacolumn-key`
@@ -89,7 +89,7 @@
 // or aux -- and any tap, since touch produces clicks) dispatches the same
 // megacolumn-key with key "Enter", so Enter, Space, and click are one event and
 // hosts serve every input modality through one handler.  A widget that already
-// activates on its own native click (sw-button in row mode) claims the click
+// activates on its own native click (yk-button in row mode) claims the click
 // with preventDefault -- the same claim idiom keys use -- and the synthesis
 // skips claimed clicks, so nothing double-fires.  Clicks on [data-mc-input]
 // never synthesize (the real-focus dive in onFocusin owns that story), and a
@@ -132,7 +132,7 @@
 // The churn lockout: when a scan finds the focused row gone and the machine's
 // fallback moves focus to a neighbor, that move was nobody's keystroke -- and a
 // decisive key already in flight must not land on the surprise neighbor
-// (the guidelines' no-race-conditions rule; the same reasoning as sw-live-list's
+// (the guidelines' no-race-conditions rule; the same reasoning as yk-live-list's
 // click guard, transposed to focus).  For FALLBACK_LOCKOUT_MS after an involuntary
 // focus move, `forward` and `enter-text` routings are consumed without dispatch;
 // navigation keys stay live, because reorienting is exactly what the user needs --
@@ -163,7 +163,7 @@
 import type { ReactiveController, ReactiveControllerHost } from "lit";
 import { css } from "lit";
 import { MegacolumnNav, type NavRow, type RowPosition } from "./megacolumn-nav.js";
-import { dispatchSwInfo } from "./yk-info.js";
+import { dispatchYkInfo } from "./yk-info.js";
 
 export interface MegacolumnKeyDetail {
 	key: string;
@@ -505,13 +505,13 @@ export class MegacolumnController implements ReactiveController {
 				}
 				// Still unresolved and the key is i: the container answers for plain
 				// [data-info] carriers (the info norm's "almost any element"; widgets
-				// with their own i handling, like sw-button, claimed above and never
+				// with their own i handling, like yk-button, claimed above and never
 				// reach this).  Nearest carrier from the focused control up to the row,
 				// inclusive: a row must not inherit info from outside itself.
 				if (!consumed && e.key.toLowerCase() === "i") {
 					const carrier = this.infoCarrier(control, row);
 					if (carrier !== null) {
-						dispatchSwInfo(carrier, carrier.dataset.info ?? "");
+						dispatchYkInfo(carrier, carrier.dataset.info ?? "");
 						claimed = true;
 						consumed = true;
 					}
@@ -588,7 +588,7 @@ export class MegacolumnController implements ReactiveController {
 	// keystrokes from surprise focus moves, while a click names its target
 	// geometrically and its own mousedown just resolved the aim.  Moving-target
 	// clicks are the geometry owner's problem, stopped upstream in the capture
-	// phase (sw-live-list's click guard) before this bubble listener can hear them.
+	// phase (yk-live-list's click guard) before this bubble listener can hear them.
 	private onClick(e: MouseEvent): void {
 		if (e.defaultPrevented) return; // a widget activated on this click already and claimed it
 		const target = e.target;
@@ -708,32 +708,32 @@ export const megacolumnStyles = css`
 		outline: none; /* the ring is drawn on the focused row instead */
 	}
 	/* Navigation scrolls (scrollIntoView from applyFocusState) land the focused row
-	   --sw-scroll-peek inside the scrollport edge, keeping about one neighbor row
+	   --yk-scroll-peek inside the scrollport edge, keeping about one neighbor row
 	   visible beyond it: the user sees the list continue (or truly end) past where
 	   they are.  scroll-margin only affects programmatic scrolls into view; free
 	   scrolling and PageUp/Down are untouched. */
 	[data-mc-row] {
-		scroll-margin-block: var(--sw-scroll-peek);
+		scroll-margin-block: var(--yk-scroll-peek);
 	}
 	[data-mc-container].mc-empty-focus {
-		outline: var(--sw-focus-ring);
-		outline-offset: calc(-1 * var(--sw-focus-ring-width));
+		outline: var(--yk-focus-ring);
+		outline-offset: calc(-1 * var(--yk-focus-ring-width));
 	}
 	.mc-focus-within [data-mc-row].mc-row-focused {
-		outline: var(--sw-focus-ring);
-		outline-offset: calc(-1 * var(--sw-focus-ring-width));
+		outline: var(--yk-focus-ring);
+		outline-offset: calc(-1 * var(--yk-focus-ring-width));
 	}
 	.mc-focus-within [data-mc-aux].mc-focused {
-		outline: var(--sw-focus-ring);
-		outline-offset: calc(-1 * var(--sw-focus-ring-width));
+		outline: var(--yk-focus-ring);
+		outline-offset: calc(-1 * var(--yk-focus-ring-width));
 	}
 	/* Focus memory stays quietly visible while the container is unfocused,
 	   so the user can see where focus will resume before tabbing back in. */
 	.mc-row-focused {
-		background: var(--sw-wash-faint);
+		background: var(--yk-wash-faint);
 	}
 	.mc-focus-within .mc-row-focused {
-		background: var(--sw-wash-selected);
+		background: var(--yk-wash-selected);
 	}
 	/* Activation feedback for plain rows: a host that acts on a forwarded
 	   activation calls flashPress on the event's target -- the row's control
@@ -742,8 +742,8 @@ export const megacolumnStyles = css`
 	   "what did I just do", inside the row the ring outlines.  Equal specificity,
 	   later in the sheet: the flash outranks the focus washes above, while the
 	   ring (an outline) coexists untouched. */
-	[data-mc-row].sw-pressed,
-	[data-mc-row] [data-mc-main].sw-pressed {
-		background: var(--sw-wash-pressed);
+	[data-mc-row].yk-pressed,
+	[data-mc-row] [data-mc-main].yk-pressed {
+		background: var(--yk-wash-pressed);
 	}
 `;
diff --git a/packages/yukey/src/megacolumn.gallery.ts b/packages/yukey/src/megacolumn.gallery.ts
index 4db74d3..90ab863 100644
--- a/packages/yukey/src/megacolumn.gallery.ts
+++ b/packages/yukey/src/megacolumn.gallery.ts
@@ -34,8 +34,8 @@ const systemActions = new Map<string, string>([
 // One demo megacolumn: activatable rows, a slider row, a two-control cargo list,
 // a text-entry row, a disabled row, and a live-churning task queue -- with section
 // gaps between them that focus jumps across.
-@customElement("swg-megacolumn-demo")
-export class SwgMegacolumnDemo extends LitElement {
+@customElement("ykg-megacolumn-demo")
+export class YkgMegacolumnDemo extends LitElement {
 	static styles = [
 		megacolumnStyles,
 		css`
@@ -43,22 +43,22 @@ export class SwgMegacolumnDemo extends LitElement {
 				display: block;
 			}
 			[data-mc-container] {
-				border: 1px solid var(--sw-border-dim);
-				padding: var(--sw-space-md);
+				border: 1px solid var(--yk-border-dim);
+				padding: var(--yk-space-md);
 			}
 			.title {
-				color: var(--sw-text-dim);
-				font-size: var(--sw-type-small);
-				letter-spacing: var(--sw-tracking-wide);
+				color: var(--yk-text-dim);
+				font-size: var(--yk-type-small);
+				letter-spacing: var(--yk-tracking-wide);
 				text-transform: uppercase;
-				margin-bottom: var(--sw-space-sm);
+				margin-bottom: var(--yk-space-sm);
 			}
 			.section {
-				color: var(--sw-text-faint);
-				font-size: var(--sw-type-micro);
-				letter-spacing: var(--sw-tracking-wide);
+				color: var(--yk-text-faint);
+				font-size: var(--yk-type-micro);
+				letter-spacing: var(--yk-tracking-wide);
 				text-transform: uppercase;
-				margin: var(--sw-space-xl) 0 var(--sw-space-xs);
+				margin: var(--yk-space-xl) 0 var(--yk-space-xs);
 			}
 			.section:first-of-type {
 				margin-top: 0;
@@ -66,47 +66,47 @@ export class SwgMegacolumnDemo extends LitElement {
 			[data-mc-row] {
 				display: flex;
 				align-items: center;
-				gap: var(--sw-space-sm);
-				min-height: var(--sw-hit-min-h);
-				padding: 2px var(--sw-space-sm);
+				gap: var(--yk-space-sm);
+				min-height: var(--yk-hit-min-h);
+				padding: 2px var(--yk-space-sm);
 			}
 			[data-mc-row].offline {
-				color: var(--sw-text-faint);
+				color: var(--yk-text-faint);
 			}
 			.grow {
 				flex: 1;
 			}
 			.count {
-				color: var(--sw-text-faint);
+				color: var(--yk-text-faint);
 			}
 			/* The hotkey-marking norm's gold letter, marked by hand on these plain
-			   rows (sw-button renders its own; plain rows have no widget to do it). */
+			   rows (yk-button renders its own; plain rows have no widget to do it). */
 			.hk {
-				color: var(--sw-hotkey-accent);
+				color: var(--yk-hotkey-accent);
 			}
 			.meter {
 				flex: 1;
 				/* the slider's whole visible affordance: at least the indicator
 				   token tall (the ant rule covers the visible part of a control) */
-				height: var(--sw-hit-indicator);
-				border: 1px solid var(--sw-border-dim);
+				height: var(--yk-hit-indicator);
+				border: 1px solid var(--yk-border-dim);
 				position: relative;
 			}
 			.meter .fill {
 				position: absolute;
 				inset: 0 auto 0 0;
-				background: var(--sw-accent);
+				background: var(--yk-accent);
 			}
 			.pct {
-				color: var(--sw-text-hi);
+				color: var(--yk-text-hi);
 				width: 4ch;
 				text-align: right;
 			}
 			[data-mc-aux] {
-				color: var(--sw-danger);
+				color: var(--yk-danger);
 				border: 1px solid transparent;
-				min-width: var(--sw-hit-min-w);
-				min-height: var(--sw-hit-min-h);
+				min-width: var(--yk-hit-min-w);
+				min-height: var(--yk-hit-min-h);
 				display: inline-flex;
 				align-items: center;
 				justify-content: center;
@@ -114,21 +114,21 @@ export class SwgMegacolumnDemo extends LitElement {
 			}
 			input[data-mc-input] {
 				flex: 1;
-				background: var(--sw-bg-well);
-				border: 1px solid var(--sw-border-dim);
-				color: var(--sw-text-bright);
+				background: var(--yk-bg-well);
+				border: 1px solid var(--yk-border-dim);
+				color: var(--yk-text-bright);
 				font: inherit;
-				min-height: var(--sw-hit-min-h);
+				min-height: var(--yk-hit-min-h);
 				box-sizing: border-box;
-				padding: 0 var(--sw-space-sm);
+				padding: 0 var(--yk-space-sm);
 			}
 			.mc-text-mode input[data-mc-input] {
-				border-color: var(--sw-accent);
+				border-color: var(--yk-accent);
 			}
 			.log {
-				margin-top: var(--sw-space-md);
-				color: var(--sw-good);
-				font-size: var(--sw-type-ui);
+				margin-top: var(--yk-space-md);
+				color: var(--yk-good);
+				font-size: var(--yk-type-ui);
 				min-height: 1.4em;
 			}
 		`,
@@ -384,8 +384,8 @@ function relayLabel(nn: string): TemplateResult {
 	return html`${label.slice(0, at)}<span class="hk">${hk}</span>${label.slice(at + 1)}`;
 }
 
-@customElement("swg-megacolumn-scroll-demo")
-export class SwgMegacolumnScrollDemo extends LitElement {
+@customElement("ykg-megacolumn-scroll-demo")
+export class YkgMegacolumnScrollDemo extends LitElement {
 	static styles = [
 		megacolumnStyles,
 		css`
@@ -393,11 +393,11 @@ export class SwgMegacolumnScrollDemo extends LitElement {
 				display: block;
 			}
 			.title {
-				color: var(--sw-text-dim);
-				font-size: var(--sw-type-small);
-				letter-spacing: var(--sw-tracking-wide);
+				color: var(--yk-text-dim);
+				font-size: var(--yk-type-small);
+				letter-spacing: var(--yk-tracking-wide);
 				text-transform: uppercase;
-				margin-bottom: var(--sw-space-sm);
+				margin-bottom: var(--yk-space-sm);
 			}
 			/* The bounded scrollport under test: the container IS the scroller,
 			   so the focused element's own box answers native scrolling keys too.
@@ -407,21 +407,21 @@ export class SwgMegacolumnScrollDemo extends LitElement {
 			[data-mc-container] {
 				height: 240px;
 				overflow-y: auto;
-				border: 1px solid var(--sw-border);
+				border: 1px solid var(--yk-border);
 			}
 			[data-mc-row] {
 				display: flex;
 				align-items: center;
-				min-height: var(--sw-hit-min-h);
-				padding: 2px var(--sw-space-sm);
+				min-height: var(--yk-hit-min-h);
+				padding: 2px var(--yk-space-sm);
 			}
 			.hk {
-				color: var(--sw-hotkey-accent);
+				color: var(--yk-hotkey-accent);
 			}
 			.log {
-				margin-top: var(--sw-space-md);
-				color: var(--sw-good);
-				font-size: var(--sw-type-ui);
+				margin-top: var(--yk-space-md);
+				color: var(--yk-good);
+				font-size: var(--yk-type-ui);
 				min-height: 1.4em;
 			}
 		`,
@@ -494,8 +494,8 @@ const entry: GalleryEntry = {
 			render: () =>
 				html`
 				<div style="display:flex; gap:24px; align-items:flex-start;">
-					<swg-megacolumn-demo style="flex:1" label="port console"></swg-megacolumn-demo>
-					<swg-megacolumn-demo style="flex:1" label="starboard console"></swg-megacolumn-demo>
+					<ykg-megacolumn-demo style="flex:1" label="port console"></ykg-megacolumn-demo>
+					<ykg-megacolumn-demo style="flex:1" label="starboard console"></ykg-megacolumn-demo>
 				</div>
 			`,
 		},
@@ -503,7 +503,7 @@ const entry: GalleryEntry = {
 			caption: "Focus memory under churn: the task queue loses its head and grows a tail every few seconds.  "
 				+ "Focus a task row and watch focus fall to the nearest surviving neighbor when the row vanishes; "
 				+ "the rest of the column is unbothered.",
-			render: () => html`<swg-megacolumn-demo label="ops console" churn></swg-megacolumn-demo>`,
+			render: () => html`<ykg-megacolumn-demo label="ops console" churn></ykg-megacolumn-demo>`,
 		},
 		{
 			caption:
@@ -513,7 +513,7 @@ const entry: GalleryEntry = {
 				+ "the gold digits jump -- 1/0/2/8 land on relays 01/10/20/28, crossing most of the scroller in one keystroke, "
 				+ "the ring arriving in view every time.  "
 				+ "Enter pings a relay; a few rows carry data-info, so i works mid-scroll.",
-			render: () => html`<swg-megacolumn-scroll-demo></swg-megacolumn-scroll-demo>`,
+			render: () => html`<ykg-megacolumn-scroll-demo></ykg-megacolumn-scroll-demo>`,
 		},
 	],
 };
diff --git a/packages/yukey/src/press-flash.test.ts b/packages/yukey/src/press-flash.test.ts
index 14d9e94..fcebb29 100644
--- a/packages/yukey/src/press-flash.test.ts
+++ b/packages/yukey/src/press-flash.test.ts
@@ -2,7 +2,7 @@
 //
 // Tests for the shared activation-flash helper (press-flash.ts).  The full
 // consumer behavior (pointer-held state, disabled suppression, re-arm) is
-// exercised through sw-button in sw-button.test.ts; this file pins the helper's
+// exercised through yk-button in yk-button.test.ts; this file pins the helper's
 // own contract: cascade-resolved duration and the fail-closed resolution error.
 
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
@@ -12,8 +12,8 @@ import { installTokens, tokenGroups } from "./tokens.js";
 installTokens();
 
 const PRESS_MS = ((): number => {
-	const t = tokenGroups.flatMap((g) => g.tokens).find((t) => t.name === "--sw-press-ms");
-	if (t === undefined) throw new Error("--sw-press-ms token missing");
+	const t = tokenGroups.flatMap((g) => g.tokens).find((t) => t.name === "--yk-press-ms");
+	if (t === undefined) throw new Error("--yk-press-ms token missing");
 	return Number.parseFloat(t.value);
 })();
 
@@ -30,20 +30,20 @@ describe("press-flash", () => {
 		const el = document.createElement("div");
 		document.body.append(el);
 		flashPress(el);
-		expect(el.classList.contains("sw-pressed")).toBe(true);
+		expect(el.classList.contains("yk-pressed")).toBe(true);
 		vi.advanceTimersByTime(PRESS_MS);
-		expect(el.classList.contains("sw-pressed")).toBe(false);
+		expect(el.classList.contains("yk-pressed")).toBe(false);
 	});
 
 	it("resolves seconds-denominated overrides too", () => {
 		const el = document.createElement("div");
-		el.style.setProperty("--sw-press-ms", "0.3s");
+		el.style.setProperty("--yk-press-ms", "0.3s");
 		document.body.append(el);
 		expect(pressMs(el)).toBe(300);
 	});
 
 	it("fails loudly when the duration cannot resolve", () => {
 		const detached = document.createElement("div"); // no cascade to read from
-		expect(() => pressMs(detached)).toThrow(/--sw-press-ms did not resolve/);
+		expect(() => pressMs(detached)).toThrow(/--yk-press-ms did not resolve/);
 	});
 });
diff --git a/packages/yukey/src/press-flash.ts b/packages/yukey/src/press-flash.ts
index 7d84403..0db8626 100644
--- a/packages/yukey/src/press-flash.ts
+++ b/packages/yukey/src/press-flash.ts
@@ -1,28 +1,28 @@
-// Activation feedback, shared across widgets: the "sw-pressed" flash class that
-// acknowledges a successful activation for --sw-press-ms, so keyboard triggers
+// Activation feedback, shared across widgets: the "yk-pressed" flash class that
+// acknowledges a successful activation for --yk-press-ms, so keyboard triggers
 // (Enter/Space, hotkeys) are as visible as a mouse press.  The duration is one
 // system-wide design token, read from the live cascade at flash time -- so an
 // accessibility mode can slow every flash by restyling one custom property.
 //
 // Consumers and their class carriers:
-//   - sw-button flashes its host element (and manages its own pointer-held
-//     "sw-held" class beside it); its styles render both.
+//   - yk-button flashes its host element (and manages its own pointer-held
+//     "yk-held" class beside it); its styles render both.
 //   - Megacolumn hosts with plain activatable rows call flashPress(rowElement)
-//     when they act on a forwarded activation; the row-flavored .sw-pressed
+//     when they act on a forwarded activation; the row-flavored .yk-pressed
 //     rendering lives in megacolumnStyles, beside the focus washes it must outrank.
 
-export const PRESSED_CLASS = "sw-pressed";
+export const PRESSED_CLASS = "yk-pressed";
 
 // The flash duration, resolved from the cascade at el.  Throws when the property
 // does not resolve (tokens not installed, or a detached element): a feedback
 // system that silently picks its own duration would hide exactly the
 // misconfiguration it exists to surface.
 export function pressMs(el: Element): number {
-	const raw = getComputedStyle(el).getPropertyValue("--sw-press-ms").trim();
+	const raw = getComputedStyle(el).getPropertyValue("--yk-press-ms").trim();
 	const m = /^(\d+(?:\.\d+)?)(m?s)$/.exec(raw);
 	if (m === null) {
 		throw new Error(
-			`press-flash: --sw-press-ms did not resolve on <${el.tagName.toLowerCase()}> `
+			`press-flash: --yk-press-ms did not resolve on <${el.tagName.toLowerCase()}> `
 				+ `(got "${raw}"); are the design tokens installed?`,
 		);
 	}
@@ -32,7 +32,7 @@ export function pressMs(el: Element): number {
 
 const timers = new WeakMap<HTMLElement, number>();
 
-// Show the pressed look on el for --sw-press-ms.  A re-flash while one is still
+// Show the pressed look on el for --yk-press-ms.  A re-flash while one is still
 // showing re-arms the timer, so rapid activations read as sustained activity
 // instead of getting cut short by the first flash's expiry.
 export function flashPress(el: HTMLElement): void {
diff --git a/packages/yukey/src/tab-cycle.ts b/packages/yukey/src/tab-cycle.ts
index 6d7cb8e..e43c250 100644
--- a/packages/yukey/src/tab-cycle.ts
+++ b/packages/yukey/src/tab-cycle.ts
@@ -11,7 +11,7 @@
 // megacolumn containers and native buttons, all visible by construction, and
 // staying layout-free keeps the walk cheap and jsdom-testable.  aria-hidden
 // subtrees are skipped: components here hide closed content that way
-// (sw-live-list's collapsing rows), and a tab stop invisible to AT must not be
+// (yk-live-list's collapsing rows), and a tab stop invisible to AT must not be
 // a tab stop at all.
 
 function isTabbable(el: Element): boolean {
diff --git a/packages/yukey/src/tokens.gallery.ts b/packages/yukey/src/tokens.gallery.ts
index 7db5667..db9207a 100644
--- a/packages/yukey/src/tokens.gallery.ts
+++ b/packages/yukey/src/tokens.gallery.ts
@@ -12,47 +12,47 @@ import { type TokenDef, tokenGroups } from "./tokens.js";
 
 // One demo element, four faces (palette / spacing / type / focus): the variants
 // below pick a face.  A single element keeps the styles in one block.
-@customElement("swg-tokens")
-export class SwgTokens extends LitElement {
+@customElement("ykg-tokens")
+export class YkgTokens extends LitElement {
 	static styles = css`
 		:host {
 			display: block;
-			font: var(--sw-type-body) var(--sw-font);
-			color: var(--sw-text);
+			font: var(--yk-type-body) var(--yk-font);
+			color: var(--yk-text);
 		}
 		.group {
-			margin-bottom: var(--sw-space-xl);
+			margin-bottom: var(--yk-space-xl);
 		}
 		.group:last-child {
 			margin-bottom: 0;
 		}
 		.group-title {
-			color: var(--sw-text-dim);
-			letter-spacing: var(--sw-tracking-wide);
+			color: var(--yk-text-dim);
+			letter-spacing: var(--yk-tracking-wide);
 			text-transform: uppercase;
-			font-size: var(--sw-type-small);
-			border-bottom: 1px solid var(--sw-border-dim);
-			padding-bottom: var(--sw-space-xs);
-			margin-bottom: var(--sw-space-xs);
+			font-size: var(--yk-type-small);
+			border-bottom: 1px solid var(--yk-border-dim);
+			padding-bottom: var(--yk-space-xs);
+			margin-bottom: var(--yk-space-xs);
 		}
 		.group-about {
-			color: var(--sw-text-faint);
+			color: var(--yk-text-faint);
 			max-width: 70ch;
-			margin-bottom: var(--sw-space-md);
+			margin-bottom: var(--yk-space-md);
 		}
 
 		/* palette rows: swatch | name | value | role */
 		.trow {
 			display: grid;
 			grid-template-columns: 72px 220px minmax(120px, 320px) 1fr;
-			gap: var(--sw-space-md);
+			gap: var(--yk-space-md);
 			align-items: center;
 			min-height: 28px;
-			margin-bottom: var(--sw-space-xs);
+			margin-bottom: var(--yk-space-xs);
 		}
 		.swatch {
 			height: 28px;
-			border: 1px solid var(--sw-border-dim);
+			border: 1px solid var(--yk-border-dim);
 			/* checkerboard underlay so translucent washes read as translucent */
 			background:
 				repeating-conic-gradient(#2a3448 0 25%, #141c2c 0 50%) 0 0 / 14px 14px;
@@ -61,42 +61,42 @@ export class SwgTokens extends LitElement {
 			height: 100%;
 		}
 		.tname {
-			color: var(--sw-text-hi);
+			color: var(--yk-text-hi);
 			user-select: all; /* one click selects the whole name, ready to copy */
 		}
 		.tvalue {
-			color: var(--sw-text-faint);
-			font-size: var(--sw-type-small);
+			color: var(--yk-text-faint);
+			font-size: var(--yk-type-small);
 			overflow-wrap: anywhere;
 		}
 		.tabout {
-			color: var(--sw-text-dim);
+			color: var(--yk-text-dim);
 		}
 
 		/* spacing & hit-target bars */
 		.bar {
 			height: 14px;
-			background: var(--sw-accent);
+			background: var(--yk-accent);
 		}
 		.hitbox {
 			display: inline-flex;
 			align-items: center;
 			justify-content: center;
-			min-height: var(--sw-hit-min-h);
-			min-width: var(--sw-hit-min-w);
-			border: 1px solid var(--sw-border);
-			color: var(--sw-text-dim);
+			min-height: var(--yk-hit-min-h);
+			min-width: var(--yk-hit-min-w);
+			border: 1px solid var(--yk-border);
+			color: var(--yk-text-dim);
 		}
 		.antbox {
 			display: inline-flex;
 			width: 8px;
 			height: 8px;
-			border: 1px solid var(--sw-warn);
+			border: 1px solid var(--yk-warn);
 		}
 
 		/* type specimens */
 		.specimen {
-			color: var(--sw-text-bright);
+			color: var(--yk-text-bright);
 			white-space: nowrap;
 			overflow: hidden;
 			text-overflow: ellipsis;
@@ -105,34 +105,34 @@ export class SwgTokens extends LitElement {
 		/* focus & hotkey demos */
 		button.demo {
 			background: none;
-			border: 1px solid var(--sw-border);
-			color: var(--sw-text);
+			border: 1px solid var(--yk-border);
+			color: var(--yk-text);
 			font: inherit;
 			cursor: pointer;
-			min-height: var(--sw-hit-min-h);
-			min-width: var(--sw-hit-min-w);
-			padding: 2px var(--sw-space-md);
+			min-height: var(--yk-hit-min-h);
+			min-width: var(--yk-hit-min-w);
+			padding: 2px var(--yk-space-md);
 		}
 		button.demo:hover {
-			color: var(--sw-text-bright);
-			border-color: var(--sw-border-bright);
+			color: var(--yk-text-bright);
+			border-color: var(--yk-border-bright);
 		}
 		button.demo:focus-visible {
-			outline: var(--sw-focus-ring);
-			outline-offset: var(--sw-focus-ring-offset);
+			outline: var(--yk-focus-ring);
+			outline-offset: var(--yk-focus-ring-offset);
 		}
 		.ring-always {
-			outline: var(--sw-focus-ring);
-			outline-offset: var(--sw-focus-ring-offset);
+			outline: var(--yk-focus-ring);
+			outline-offset: var(--yk-focus-ring-offset);
 		}
 		.demo-row {
 			display: flex;
 			align-items: center;
-			gap: var(--sw-space-lg);
-			margin: var(--sw-space-md) 0 var(--sw-space-lg);
+			gap: var(--yk-space-lg);
+			margin: var(--yk-space-md) 0 var(--yk-space-lg);
 		}
 		.note {
-			color: var(--sw-text-faint);
+			color: var(--yk-text-faint);
 			max-width: 70ch;
 		}
 	`;
@@ -172,7 +172,7 @@ export class SwgTokens extends LitElement {
 					html`<div class="group">
 				<div class="group-title">${g.title}</div>
 				<div class="group-about">${g.about}</div>
-				${g.tokens.map((t) => SwgTokens.paletteRow(t))}
+				${g.tokens.map((t) => YkgTokens.paletteRow(t))}
 			</div>`,
 			)
 		}`;
@@ -218,8 +218,8 @@ export class SwgTokens extends LitElement {
 	private renderType(): TemplateResult {
 		const type = tokenGroups.find((g) => g.id === "type");
 		if (type === undefined) return html`missing group`;
-		const sizes = type.tokens.filter((t) => t.name.startsWith("--sw-type-"));
-		const rest = type.tokens.filter((t) => !t.name.startsWith("--sw-type-"));
+		const sizes = type.tokens.filter((t) => t.name.startsWith("--yk-type-"));
+		const rest = type.tokens.filter((t) => !t.name.startsWith("--yk-type-"));
 		return html`<div class="group">
 			<div class="group-title">${type.title}</div>
 			<div class="group-about">${type.about}</div>
@@ -263,20 +263,20 @@ export class SwgTokens extends LitElement {
 				<div class="note">
 					The first two show the ring only on keyboard focus (:focus-visible);
 					the third wears it permanently for inspection.
-					Composite: --sw-focus-ring, offset by --sw-focus-ring-offset.
+					Composite: --yk-focus-ring, offset by --yk-focus-ring-offset.
 				</div>
 			</div>
 			<div class="group">
 				<div class="group-title">hotkey marking</div>
 				<div class="demo-row">
-					<sw-button label="dock" hotkey="d"></sw-button>
-					<sw-button label="engage" hotkey="7"></sw-button>
+					<yk-button label="dock" hotkey="d"></yk-button>
+					<yk-button label="engage" hotkey="7"></yk-button>
 				</div>
 				<div class="note">
-					The guidelines' button-marking norm, in --sw-hotkey-accent:
+					The guidelines' button-marking norm, in --yk-hotkey-accent:
 					letter-in-label when the hotkey appears in the label,
 					else the letter in a slot 70% of the way across.
-					These are live sw-buttons; the full behavior tour is the sw-button entry.
+					These are live yk-buttons; the full behavior tour is the yk-button entry.
 				</div>
 			</div>
 			<div class="group">
@@ -293,11 +293,11 @@ export class SwgTokens extends LitElement {
 			)
 		}
 				<div class="demo-row">
-					<sw-button label="press me" hotkey="p"></sw-button>
+					<yk-button label="press me" hotkey="p"></yk-button>
 					<span class="note">
 						Every activation (click, Enter/Space, hotkey) flashes the pressed look
-						for --sw-press-ms; holding the mouse down shows the same look for as
-						long as it is held.  The wash is --sw-wash-pressed.
+						for --yk-press-ms; holding the mouse down shows the same look for as
+						long as it is held.  The wash is --yk-wash-pressed.
 					</span>
 				</div>
 			</div>
@@ -308,7 +308,7 @@ export class SwgTokens extends LitElement {
 const entry: GalleryEntry = {
 	id: "tokens",
 	title: "design tokens",
-	description: "The visual vocabulary everything else is built from: --sw-* custom properties, "
+	description: "The visual vocabulary everything else is built from: --yk-* custom properties, "
 		+ "rendered live from their definitions in tokens.ts.  Names are semantic roles and are "
 		+ "the durable part; values are the iteration surface -- edit tokens.ts, rebuild, "
 		+ "and this page shows the new truth.  Intent notes live in docs/precepts-aesthetics.md.",
@@ -317,23 +317,23 @@ const entry: GalleryEntry = {
 			caption: "The palette, by role.  Swatches sit on a checkerboard so translucent washes read as "
 				+ "translucent; wash values derive from their base color, so retinting a base retints its washes.",
 			wide: true,
-			render: () => html`<swg-tokens face="palette"></swg-tokens>`,
+			render: () => html`<ykg-tokens face="palette"></ykg-tokens>`,
 		},
 		{
 			caption: "The spacing ramp (gaps carry meaning) and the hit-target minimums.",
 			wide: true,
-			render: () => html`<swg-tokens face="spacing"></swg-tokens>`,
+			render: () => html`<ykg-tokens face="spacing"></ykg-tokens>`,
 		},
 		{
 			caption: "The type ramp and tracking values, each rendered at its own size.",
 			wide: true,
-			render: () => html`<swg-tokens face="type"></swg-tokens>`,
+			render: () => html`<ykg-tokens face="type"></ykg-tokens>`,
 		},
 		{
 			caption: "Keyboard visibility: the focus ring (try tabbing through), the two hotkey-marking forms, "
 				+ "and the activation flash (press things; watch them acknowledge).",
 			wide: true,
-			render: () => html`<swg-tokens face="focus"></swg-tokens>`,
+			render: () => html`<ykg-tokens face="focus"></ykg-tokens>`,
 		},
 	],
 };
diff --git a/packages/yukey/src/tokens.test.ts b/packages/yukey/src/tokens.test.ts
index 892d969..1603312 100644
--- a/packages/yukey/src/tokens.test.ts
+++ b/packages/yukey/src/tokens.test.ts
@@ -10,9 +10,9 @@ import { tokenGroups, tokensCssText } from "./tokens.js";
 describe("the token vocabulary", () => {
 	const all = tokenGroups.flatMap((g) => g.tokens);
 
-	it("names every token in the --sw- namespace, lowercase-and-dashes only", () => {
+	it("names every token in the --yk- namespace, lowercase-and-dashes only", () => {
 		for (const t of all) {
-			expect(t.name).toMatch(/^--sw-[a-z][a-z0-9-]*$/);
+			expect(t.name).toMatch(/^--yk-[a-z][a-z0-9-]*$/);
 		}
 	});
 
diff --git a/packages/yukey/src/tokens.ts b/packages/yukey/src/tokens.ts
index 5d9fa1d..9b54a0c 100644
--- a/packages/yukey/src/tokens.ts
+++ b/packages/yukey/src/tokens.ts
@@ -6,7 +6,7 @@
 // Tokens are defined as data (tokenGroups) and the :root stylesheet is generated
 // from it, so the stylesheet and the gallery's living documentation can never
 // disagree.  Both entry points call installTokens(); custom properties inherit
-// through shadow boundaries, so components just write `var(--sw-...)`.
+// through shadow boundaries, so components just write `var(--yk-...)`.
 //
 // Naming is the durable part: tokens are named for their semantic role, so values
 // can be iterated hard (they will be) while the meaning holds still.  The values
@@ -21,7 +21,7 @@ import { unsafeCSS } from "lit";
 export type TokenKind = "color" | "size" | "font" | "composite" | "duration";
 
 export interface TokenDef {
-	name: `--sw-${string}`;
+	name: `--yk-${string}`;
 	value: string;
 	kind: TokenKind;
 	// The role, in a sentence: what kind of thing should use this token.
@@ -44,31 +44,31 @@ export const tokenGroups: TokenGroup[] = [
 		about: "Background layers, from the void of space up to floating overlays.",
 		tokens: [
 			{
-				name: "--sw-bg-void",
+				name: "--yk-bg-void",
 				value: "#05080f",
 				kind: "color",
 				about: "the deepest layer: the page behind everything",
 			},
 			{
-				name: "--sw-bg-panel-solid",
+				name: "--yk-bg-panel-solid",
 				value: "#0a1220",
 				kind: "color",
 				about: "opaque panel surface: cards, sticky headers, toasts -- anywhere bleed-through would muddy",
 			},
 			{
-				name: "--sw-bg-panel",
-				value: "color-mix(in srgb, var(--sw-bg-panel-solid) 94%, transparent)",
+				name: "--yk-bg-panel",
+				value: "color-mix(in srgb, var(--yk-bg-panel-solid) 94%, transparent)",
 				kind: "color",
 				about: "translucent panel surface: big workspace panels floating over the page",
 			},
 			{
-				name: "--sw-bg-well",
+				name: "--yk-bg-well",
 				value: "#0d1626",
 				kind: "color",
 				about: "inset areas within a panel: chart plots, code samples",
 			},
 			{
-				name: "--sw-bg-raised",
+				name: "--yk-bg-raised",
 				value: "#101a2c",
 				kind: "color",
 				about: "small floating surfaces above panel content: tooltips, popovers",
@@ -81,19 +81,19 @@ export const tokenGroups: TokenGroup[] = [
 		about: "Panel edges and separators.  Brightness tracks prominence.",
 		tokens: [
 			{
-				name: "--sw-border",
+				name: "--yk-border",
 				value: "#3d5578",
 				kind: "color",
 				about: "the standard chrome border: panel edges, buttons, separator bars",
 			},
 			{
-				name: "--sw-border-dim",
+				name: "--yk-border-dim",
 				value: "#1c2a40",
 				kind: "color",
 				about: "subtle borders: section rules, chart frames, quiet separations",
 			},
 			{
-				name: "--sw-border-bright",
+				name: "--yk-border-bright",
 				value: "#5a7290",
 				kind: "color",
 				about: "emphasized borders: hover states on bordered controls",
@@ -106,31 +106,31 @@ export const tokenGroups: TokenGroup[] = [
 		about: "The gray-blue text ramp, five tiers from emphasized to nearly-ambient.",
 		tokens: [
 			{
-				name: "--sw-text-bright",
+				name: "--yk-text-bright",
 				value: "#dfe9f5",
 				kind: "color",
 				about: "primary emphasis: names, values, hover text -- what the eye should land on",
 			},
 			{
-				name: "--sw-text-hi",
+				name: "--yk-text-hi",
 				value: "#a8bdd8",
 				kind: "color",
 				about: "secondary emphasis: entity names in rows, highlighted prose",
 			},
 			{
-				name: "--sw-text",
+				name: "--yk-text",
 				value: "#8194ac",
 				kind: "color",
 				about: "body text: the default reading color",
 			},
 			{
-				name: "--sw-text-dim",
+				name: "--yk-text-dim",
 				value: "#5a7290",
 				kind: "color",
 				about: "dim text: labels, section headings, quiet chrome",
 			},
 			{
-				name: "--sw-text-faint",
+				name: "--yk-text-faint",
 				value: "#48566b",
 				kind: "color",
 				about: "faint text: ids, empty states, the almost-not-there tier",
@@ -144,40 +144,40 @@ export const tokenGroups: TokenGroup[] = [
 			+ "Washes derive from the accent, so retinting it retints them.",
 		tokens: [
 			{
-				name: "--sw-accent",
+				name: "--yk-accent",
 				value: "#4f7ba8",
 				kind: "color",
 				about: "the interactive blue: meter fills, checkboxes, chart lines, attention borders",
 			},
 			{
-				name: "--sw-accent-bright",
+				name: "--yk-accent-bright",
 				value: "#8fb3d9",
 				kind: "color",
 				about: "the accent as text: app title, score, links",
 			},
 			{
-				name: "--sw-wash-pressed",
-				value: "hsl(from var(--sw-accent) h calc(s * 1.5) calc(l * 1.2) / 0.5)",
+				name: "--yk-wash-pressed",
+				value: "hsl(from var(--yk-accent) h calc(s * 1.5) calc(l * 1.2) / 0.5)",
 				kind: "color",
 				about: "background wash for a control being activated: held under a pointer, "
-					+ "or flashing for --sw-press-ms after triggering.  Deliberately more saturated "
+					+ "or flashing for --yk-press-ms after triggering.  Deliberately more saturated "
 					+ "and brighter than the accent it derives from: activation must outrank selection at a glance",
 			},
 			{
-				name: "--sw-wash-selected",
-				value: "color-mix(in srgb, var(--sw-accent) 25%, transparent)",
+				name: "--yk-wash-selected",
+				value: "color-mix(in srgb, var(--yk-accent) 25%, transparent)",
 				kind: "color",
 				about: "background wash for the selected row / the toggled-on button",
 			},
 			{
-				name: "--sw-wash-hover",
-				value: "color-mix(in srgb, var(--sw-accent) 12%, transparent)",
+				name: "--yk-wash-hover",
+				value: "color-mix(in srgb, var(--yk-accent) 12%, transparent)",
 				kind: "color",
 				about: "background wash for the hovered row",
 			},
 			{
-				name: "--sw-wash-faint",
-				value: "color-mix(in srgb, var(--sw-accent) 6%, transparent)",
+				name: "--yk-wash-faint",
+				value: "color-mix(in srgb, var(--yk-accent) 6%, transparent)",
 				kind: "color",
 				about: "the quietest wash: help strips, gently set-off regions",
 			},
@@ -189,49 +189,49 @@ export const tokenGroups: TokenGroup[] = [
 		about: "Status colors.  Teal means working, amber means stuck, red means unreachable.",
 		tokens: [
 			{
-				name: "--sw-good",
+				name: "--yk-good",
 				value: "#8fd4c0",
 				kind: "color",
 				about: "the working-economy teal: producing, positive flow, promises of movement",
 			},
 			{
-				name: "--sw-warn",
+				name: "--yk-warn",
 				value: "#c8863c",
 				kind: "color",
 				about: "the amber alert: rejected commands, blocked production, negative flow, emergencies",
 			},
 			{
-				name: "--sw-warn-border",
+				name: "--yk-warn-border",
 				value: "#6b4a24",
 				kind: "color",
 				about: "border for amber-alert surfaces (the rejection toast)",
 			},
 			{
-				name: "--sw-warn-wash",
-				value: "color-mix(in srgb, var(--sw-warn) 13%, transparent)",
+				name: "--yk-warn-wash",
+				value: "color-mix(in srgb, var(--yk-warn) 13%, transparent)",
 				kind: "color",
 				about: "background wash for rows in an emergency state",
 			},
 			{
-				name: "--sw-warn-wash-hover",
-				value: "color-mix(in srgb, var(--sw-warn) 22%, transparent)",
+				name: "--yk-warn-wash-hover",
+				value: "color-mix(in srgb, var(--yk-warn) 22%, transparent)",
 				kind: "color",
 				about: "hover wash for emergency rows",
 			},
 			{
-				name: "--sw-warn-wash-selected",
-				value: "color-mix(in srgb, var(--sw-warn) 30%, transparent)",
+				name: "--yk-warn-wash-selected",
+				value: "color-mix(in srgb, var(--yk-warn) 30%, transparent)",
 				kind: "color",
 				about: "selected wash for emergency rows",
 			},
 			{
-				name: "--sw-danger",
+				name: "--yk-danger",
 				value: "#e8a0a0",
 				kind: "color",
 				about: "the red alert: no connection, destructive controls (the row-remove x)",
 			},
 			{
-				name: "--sw-danger-border",
+				name: "--yk-danger-border",
 				value: "#7a4040",
 				kind: "color",
 				about: "border for red-alert surfaces (the no-connection toast)",
@@ -245,44 +245,44 @@ export const tokenGroups: TokenGroup[] = [
 			+ "The focus-model and button stages consume them heavily.",
 		tokens: [
 			{
-				name: "--sw-hotkey-accent",
+				name: "--yk-hotkey-accent",
 				value: "#e8c86a",
 				kind: "color",
 				about: "the significant color for hotkey marking: the letter-in-label tint, the 70%-offset hotkey slot.  "
 					+ "Deliberately the only gold in the palette: hotkeys must pop",
 			},
 			{
-				name: "--sw-selection",
+				name: "--yk-selection",
 				value: "#d8ecff",
 				kind: "color",
 				about: "the selection marker: a dashed ring around the selected entity",
 			},
 			{
-				name: "--sw-focus-ring-color",
+				name: "--yk-focus-ring-color",
 				value: "#d8ecff",
 				kind: "color",
 				about: "the keyboard focus ring: same where-you-are family as selection, drawn solid",
 			},
 			{
-				name: "--sw-focus-ring-width",
+				name: "--yk-focus-ring-width",
 				value: "2px",
 				kind: "size",
 				about: "focus ring stroke width",
 			},
 			{
-				name: "--sw-focus-ring-offset",
+				name: "--yk-focus-ring-offset",
 				value: "2px",
 				kind: "size",
 				about: "gap between a focused element and its ring (pair with outline-offset)",
 			},
 			{
-				name: "--sw-focus-ring",
-				value: "var(--sw-focus-ring-width) solid var(--sw-focus-ring-color)",
+				name: "--yk-focus-ring",
+				value: "var(--yk-focus-ring-width) solid var(--yk-focus-ring-color)",
 				kind: "composite",
 				about: "the whole ring, ready for `outline:`",
 			},
 			{
-				name: "--sw-scroll-peek",
+				name: "--yk-scroll-peek",
 				value: "28px",
 				kind: "size",
 				about: "how much neighbor stays visible past the focused row when navigation scrolls: "
@@ -298,7 +298,7 @@ export const tokenGroups: TokenGroup[] = [
 			+ "slowing one here slows it everywhere, which is what an accessibility mode needs.",
 		tokens: [
 			{
-				name: "--sw-press-ms",
+				name: "--yk-press-ms",
 				value: "140ms",
 				kind: "duration",
 				about: "how long the activation flash lingers on a triggered control.  "
@@ -314,31 +314,31 @@ export const tokenGroups: TokenGroup[] = [
 			+ "lg separates groups, xl+ separates sections (the gaps megacolumn focus jumps across).",
 		tokens: [
 			{
-				name: "--sw-space-xs",
+				name: "--yk-space-xs",
 				value: "4px",
 				kind: "size",
 				about: "within a cluster: icon-to-label",
 			},
 			{
-				name: "--sw-space-sm",
+				name: "--yk-space-sm",
 				value: "8px",
 				kind: "size",
 				about: "between related elements: button rows",
 			},
 			{
-				name: "--sw-space-md",
+				name: "--yk-space-md",
 				value: "12px",
 				kind: "size",
 				about: "panel edge padding, roomier relations",
 			},
 			{
-				name: "--sw-space-lg",
+				name: "--yk-space-lg",
 				value: "16px",
 				kind: "size",
 				about: "between groups within a section",
 			},
-			{ name: "--sw-space-xl", value: "24px", kind: "size", about: "between sections" },
-			{ name: "--sw-space-xxl", value: "32px", kind: "size", about: "the biggest deliberate gap" },
+			{ name: "--yk-space-xl", value: "24px", kind: "size", about: "between sections" },
+			{ name: "--yk-space-xxl", value: "32px", kind: "size", about: "the biggest deliberate gap" },
 		],
 	},
 	{
@@ -348,19 +348,19 @@ export const tokenGroups: TokenGroup[] = [
 			"We are not ants.  No interactable renders smaller than this -- and that includes the VISIBLE part, not just the hit zone.",
 		tokens: [
 			{
-				name: "--sw-hit-min-h",
+				name: "--yk-hit-min-h",
 				value: "24px",
 				kind: "size",
 				about: "minimum height of anything interactable",
 			},
 			{
-				name: "--sw-hit-min-w",
+				name: "--yk-hit-min-w",
 				value: "24px",
 				kind: "size",
 				about: "minimum width of anything interactable (icon-only buttons, checkboxes)",
 			},
 			{
-				name: "--sw-hit-indicator",
+				name: "--yk-hit-indicator",
 				value: "16px",
 				kind: "size",
 				about:
@@ -373,36 +373,36 @@ export const tokenGroups: TokenGroup[] = [
 		title: "type",
 		about: "Monospace-heavy by design: this is a SCADA console, not a magazine.",
 		tokens: [
-			{ name: "--sw-font", value: "monospace", kind: "font", about: "the UI font stack" },
-			{ name: "--sw-type-micro", value: "10px", kind: "size", about: "column headers, overlines" },
+			{ name: "--yk-font", value: "monospace", kind: "font", about: "the UI font stack" },
+			{ name: "--yk-type-micro", value: "10px", kind: "size", about: "column headers, overlines" },
 			{
-				name: "--sw-type-small",
+				name: "--yk-type-small",
 				value: "11px",
 				kind: "size",
 				about: "fine print: ids, section headings",
 			},
 			{
-				name: "--sw-type-ui",
+				name: "--yk-type-ui",
 				value: "12px",
 				kind: "size",
 				about: "dense chrome: bars, tables, panels",
 			},
 			{
-				name: "--sw-type-body",
+				name: "--yk-type-body",
 				value: "13px",
 				kind: "size",
 				about: "reading surfaces: cards, prose",
 			},
-			{ name: "--sw-type-head", value: "16px", kind: "size", about: "page and panel headings" },
-			{ name: "--sw-type-title", value: "18px", kind: "size", about: "the biggest: card names" },
+			{ name: "--yk-type-head", value: "16px", kind: "size", about: "page and panel headings" },
+			{ name: "--yk-type-title", value: "18px", kind: "size", about: "the biggest: card names" },
 			{
-				name: "--sw-tracking",
+				name: "--yk-tracking",
 				value: "0.08em",
 				kind: "size",
 				about: "letterspacing for emphasized titles at text size",
 			},
 			{
-				name: "--sw-tracking-wide",
+				name: "--yk-tracking-wide",
 				value: "0.12em",
 				kind: "size",
 				about: "letterspacing for uppercase overlines (section headings, app title)",
@@ -426,16 +426,16 @@ export function tokensCssText(groups: TokenGroup[]): string {
 }
 
 // unsafeCSS is safe here: the text is generated from the literal definitions in this
-// file, never from runtime data (the same argument as sw-balance's GRID_TEMPLATE).
+// file, never from runtime data (the same argument as yk-balance's GRID_TEMPLATE).
 export const tokens: CSSResult = unsafeCSS(tokensCssText(tokenGroups));
 
 // Installs the token definitions document-wide (a <style> in head).  Custom
 // properties inherit into every shadow root from there.  Idempotent: entry points
 // call it once, but a second call is harmless.
 export function installTokens(doc: Document = document): void {
-	if (doc.head.querySelector("style[data-sw-tokens]") !== null) return;
+	if (doc.head.querySelector("style[data-yk-tokens]") !== null) return;
 	const style = doc.createElement("style");
-	style.setAttribute("data-sw-tokens", "");
+	style.setAttribute("data-yk-tokens", "");
 	style.textContent = tokens.cssText;
 	doc.head.append(style);
 }
diff --git a/packages/yukey/src/yk-button.gallery.ts b/packages/yukey/src/yk-button.gallery.ts
index 63982ea..f0ad8c5 100644
--- a/packages/yukey/src/yk-button.gallery.ts
+++ b/packages/yukey/src/yk-button.gallery.ts
@@ -1,4 +1,4 @@
-// Gallery entry for sw-button: every marking form, both contexts, the disabled
+// Gallery entry for yk-button: every marking form, both contexts, the disabled
 // and info behaviors, and a two-column demo proving that hotkey scope is the
 // focused column.
 
@@ -8,7 +8,7 @@ import type { GalleryEntry } from "./gallery/entry.js";
 import { MegacolumnController, type MegacolumnKeyDetail, megacolumnStyles } from "./megacolumn-controller.js";
 import "./yk-button.js";
 import { flashPress } from "./press-flash.js";
-import type { SwInfoDetail } from "./yk-info.js";
+import type { YkInfoDetail } from "./yk-info.js";
 
 // Shared demo chrome: a title, a log line that shows what fired.
 const demoStyles = css`
@@ -16,32 +16,32 @@ const demoStyles = css`
 		display: block;
 	}
 	.title {
-		color: var(--sw-text-dim);
-		font-size: var(--sw-type-small);
-		letter-spacing: var(--sw-tracking-wide);
+		color: var(--yk-text-dim);
+		font-size: var(--yk-type-small);
+		letter-spacing: var(--yk-tracking-wide);
 		text-transform: uppercase;
-		margin-bottom: var(--sw-space-sm);
+		margin-bottom: var(--yk-space-sm);
 	}
 	.log {
-		margin-top: var(--sw-space-md);
-		color: var(--sw-good);
-		font-size: var(--sw-type-ui);
+		margin-top: var(--yk-space-md);
+		color: var(--yk-good);
+		font-size: var(--yk-type-ui);
 		min-height: 1.4em;
 	}
 	.log.info {
-		color: var(--sw-hotkey-accent);
+		color: var(--yk-hotkey-accent);
 	}
 `;
 
 // Standalone buttons: native tab stops, all marking forms side by side.
-@customElement("swg-button-standalone")
-export class SwgButtonStandalone extends LitElement {
+@customElement("ykg-button-standalone")
+export class YkgButtonStandalone extends LitElement {
 	static styles = [
 		demoStyles,
 		css`
 			.row {
 				display: flex;
-				gap: var(--sw-space-lg);
+				gap: var(--yk-space-lg);
 				align-items: center;
 				flex-wrap: wrap;
 			}
@@ -59,73 +59,73 @@ export class SwgButtonStandalone extends LitElement {
 	}
 
 	private onInfo(e: Event): void {
-		const { content } = (e as CustomEvent<SwInfoDetail>).detail;
+		const { content } = (e as CustomEvent<YkInfoDetail>).detail;
 		this.last = `info: ${content}`;
 		this.lastWasInfo = true;
 	}
 
 	render(): TemplateResult {
 		return html`
-			<div class="row" @sw-info=${this.onInfo}>
-				<sw-button
+			<div class="row" @yk-info=${this.onInfo}>
+				<yk-button
 					label="Dock at station"
 					hotkey="d"
 					info="requests docking clearance at the selected station"
 					@click=${() => this.act("docked")}
-				></sw-button>
-				<sw-button
+				></yk-button>
+				<yk-button
 					label="units"
 					hotkey="7"
 					info="opens the units registry"
 					@click=${() => this.act("units opened")}
-				></sw-button>
-				<sw-button
+				></yk-button>
+				<yk-button
 					label="transfer"
 					hotkey="t"
 					icon="☰"
 					info="complex: several transfer modes live behind this one"
 					@click=${() => this.act("transfer menu")}
-				></sw-button>
-				<sw-button
+				></yk-button>
+				<yk-button
 					label="cruise"
 					hotkey="c"
 					info="the in-effect option: active but still clickable (re-issuing is the owner's call)"
 					active
 					@click=${() => this.act("cruise re-issued (harmless)")}
-				></sw-button>
-				<sw-button
+				></yk-button>
+				<yk-button
 					label="self destruct"
 					hotkey="s"
 					info="offline: requires command authorization"
 					disabled
 					@click=${() => this.act("BOOM (should never happen)")}
-				></sw-button>
+				></yk-button>
 			</div>
 			<div class="log ${this.lastWasInfo ? "info" : ""}">${this.last}</div>
 		`;
 	}
 }
 
-// A megacolumn of sw-buttons: rows are the buttons, plus a two-control row
-// composing an sw-button into the aux (small-x) slot.
-@customElement("swg-button-column")
-export class SwgButtonColumn extends LitElement {
+// A megacolumn of yk-buttons: rows are the buttons, plus a two-control row
+// composing a yk-button into the aux (small-x) slot.
+@customElement("ykg-button-column")
+export class YkgButtonColumn extends LitElement {
 	static styles = [
 		megacolumnStyles,
 		demoStyles,
 		css`
 			[data-mc-container] {
-				border: 1px solid var(--sw-border-dim);
-				padding: var(--sw-space-md);
+				border: 1px solid var(--yk-border-dim);
+				padding: var(--yk-space-md);
 			}
 			[data-mc-row] {
 				display: flex;
 				align-items: center;
-				gap: var(--sw-space-sm);
-				min-height: var(--sw-hit-min-h);
-				padding: 2px var(--sw-space-sm);
+				gap: var(--yk-space-sm);
+				min-height: var(--yk-hit-min-h);
+				padding: 2px var(--yk-space-sm);
 			}
-			[data-mc-row] sw-button:first-child,
+			[data-mc-row] yk-button:first-child,
 			[data-mc-row] > .grow {
 				flex: 1;
 			}
@@ -157,7 +157,7 @@ export class SwgButtonColumn extends LitElement {
 	}
 
 	private onInfo(e: Event): void {
-		const { content } = (e as CustomEvent<SwInfoDetail>).detail;
+		const { content } = (e as CustomEvent<YkInfoDetail>).detail;
 		this.last = `info: ${content}`;
 		this.lastWasInfo = true;
 	}
@@ -183,31 +183,31 @@ export class SwgButtonColumn extends LitElement {
 				data-mc-container
 				aria-label=${this.label}
 				@megacolumn-key=${this.onMegacolumnKey}
-				@sw-info=${this.onInfo}
+				@yk-info=${this.onInfo}
 			>
 				<div data-mc-row="dock">
-					<sw-button
+					<yk-button
 						label="Dock at station"
 						hotkey=${this.dockKey}
 						info="requests docking clearance"
 						@click=${() => this.act("docked")}
-					></sw-button>
+					></yk-button>
 				</div>
 				<div data-mc-row="beacon">
-					<sw-button
+					<yk-button
 						label="cycle beacon"
 						hotkey=${this.beaconKey}
 						info="cycles the nav beacon"
 						@click=${() => this.act("beacon cycled")}
-					></sw-button>
+					></yk-button>
 				</div>
 				<div data-mc-row="purge">
-					<sw-button
+					<yk-button
 						label="purge filters"
 						info="offline: filters already clean"
 						disabled
 						@click=${() => this.act("purged (should never happen)")}
-					></sw-button>
+					></yk-button>
 				</div>
 				${
 			this.cargo.map(
@@ -216,14 +216,14 @@ export class SwgButtonColumn extends LitElement {
 						<div data-mc-row=${item}>
 							<span class="grow" data-mc-main>${item}</span>
 							<span data-mc-aux>
-								<sw-button
+								<yk-button
 									label="×"
 									info="jettisons this crate (x or Delete from anywhere in the row)"
 									@click=${() => {
 						this.cargo = this.cargo.filter((c) => c !== item);
 						this.act(`${item} jettisoned`);
 					}}
-								></sw-button>
+								></yk-button>
 							</span>
 						</div>
 					`,
@@ -236,12 +236,12 @@ export class SwgButtonColumn extends LitElement {
 }
 
 const entry: GalleryEntry = {
-	id: "sw-button",
-	title: "sw-button",
+	id: "yk-button",
+	title: "yk-button",
 	description: "The button that makes the keyboard norms structural: hotkey marking "
-		+ "(letter-in-label in --sw-hotkey-accent, else the 70%-offset slot, an icon for "
-		+ "complex buttons), the i info hook (sw-info events, rendered by the shell's "
-		+ "sw-infopop -- press i on anything here and watch), token hit targets, and "
+		+ "(letter-in-label in --yk-hotkey-accent, else the 70%-offset slot, an icon for "
+		+ "complex buttons), the i info hook (yk-info events, rendered by the shell's "
+		+ "yk-infopop -- press i on anything here and watch), token hit targets, and "
 		+ "dual-context behavior -- a native focusable standalone, a megacolumn-key "
 		+ "consumer inside rows.",
 	variants: [
@@ -251,7 +251,7 @@ const entry: GalleryEntry = {
 				+ "(transfer), the active in-effect option (cruise: washed, aria-current, still "
 				+ "clickable), and disabled (dimmed, unclickable, but i still answers while focused).  "
 				+ "A button's hotkey and i work while it holds focus; watch the log.",
-			render: () => html`<swg-button-standalone></swg-button-standalone>`,
+			render: () => html`<ykg-button-standalone></ykg-button-standalone>`,
 		},
 		{
 			caption: "Inside a megacolumn: one tab stop for the whole column, buttons as rows.  "
@@ -260,7 +260,7 @@ const entry: GalleryEntry = {
 				+ "before committing.  Try: d, then Enter; i for info on the focused control; "
 				+ "on a crate row, x or Delete jettisons via the aux ×, or right-arrow onto it and Enter.  "
 				+ "The disabled row still focuses and answers i, but nothing activates it.",
-			render: () => html`<swg-button-column label="ops console"></swg-button-column>`,
+			render: () => html`<ykg-button-column label="ops console"></ykg-button-column>`,
 		},
 		{
 			caption: "Hotkey scope IS the focused column: both columns mark d for dock, and d jumps only "
@@ -270,8 +270,8 @@ const entry: GalleryEntry = {
 			render: () =>
 				html`
 				<div style="display:flex; gap:24px; align-items:flex-start;">
-					<swg-button-column style="flex:1" label="port console" dockKey="d" beaconKey="b"></swg-button-column>
-					<swg-button-column style="flex:1" label="starboard console" dockKey="d" beaconKey="c"></swg-button-column>
+					<ykg-button-column style="flex:1" label="port console" dockKey="d" beaconKey="b"></ykg-button-column>
+					<ykg-button-column style="flex:1" label="starboard console" dockKey="d" beaconKey="c"></ykg-button-column>
 				</div>
 			`,
 		},
diff --git a/packages/yukey/src/yk-button.test.ts b/packages/yukey/src/yk-button.test.ts
index ebe87f2..43f7653 100644
--- a/packages/yukey/src/yk-button.test.ts
+++ b/packages/yukey/src/yk-button.test.ts
@@ -1,16 +1,16 @@
 // @vitest-environment jsdom
 //
-// Tests for sw-button (sw-button.ts): the marking logic, standalone activation,
+// Tests for yk-button (yk-button.ts): the marking logic, standalone activation,
 // and row-mode claims driven through a real MegacolumnController rig -- the same
 // fake-host pattern as megacolumn-controller.test.ts, but with live Lit rendering
-// (sw-button's marking must be visible to the controller's scan synchronously).
+// (yk-button's marking must be visible to the controller's scan synchronously).
 
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
 import { MegacolumnController } from "./megacolumn-controller.js";
-import type { SwButton } from "./yk-button.js";
+import type { YkButton } from "./yk-button.js";
 import "./yk-button.js";
 import { installTokens, tokenGroups } from "./tokens.js";
-import type { SwInfoDetail } from "./yk-info.js";
+import type { YkInfoDetail } from "./yk-info.js";
 
 // jsdom implements no scrollIntoView; the controller scrolls the focused row
 // into view on every focus identity change.  A no-op stands in.
@@ -23,27 +23,27 @@ installTokens();
 // The token's defined default, parsed here rather than imported from
 // press-flash.ts: the tests must not derive expectations from the code under test.
 const PRESS_MS = ((): number => {
-	const t = tokenGroups.flatMap((g) => g.tokens).find((t) => t.name === "--sw-press-ms");
-	if (t === undefined) throw new Error("--sw-press-ms token missing");
+	const t = tokenGroups.flatMap((g) => g.tokens).find((t) => t.name === "--yk-press-ms");
+	if (t === undefined) throw new Error("--yk-press-ms token missing");
 	return Number.parseFloat(t.value);
 })();
 
-function makeButton(attrs: Partial<SwButton>): SwButton {
-	const el = document.createElement("sw-button");
+function makeButton(attrs: Partial<YkButton>): YkButton {
+	const el = document.createElement("yk-button");
 	Object.assign(el, attrs);
 	return el;
 }
 
-async function standalone(attrs: Partial<SwButton>): Promise<SwButton> {
+async function standalone(attrs: Partial<YkButton>): Promise<YkButton> {
 	const el = makeButton(attrs);
 	document.body.replaceChildren(el);
 	await el.updateComplete;
 	return el;
 }
 
-function inner(el: SwButton): HTMLElement {
+function inner(el: YkButton): HTMLElement {
 	const btn = el.shadowRoot?.querySelector<HTMLElement>(".btn");
-	if (!btn) throw new Error("sw-button rendered no .btn");
+	if (!btn) throw new Error("yk-button rendered no .btn");
 	return btn;
 }
 
@@ -89,8 +89,8 @@ describe("hotkey marking", () => {
 	});
 
 	it("mirrors info onto data-info synchronously (the hover-trigger carrier contract)", () => {
-		// sw-infopop's hover trigger finds carriers by [data-info]; the mirror makes
-		// sw-buttons carriers without the popup knowing their class.
+		// yk-infopop's hover trigger finds carriers by [data-info]; the mirror makes
+		// yk-buttons carriers without the popup knowing their class.
 		const el = makeButton({ info: "docking clearance" });
 		expect(el.dataset.info).toBe("docking clearance"); // before any render or connect
 		el.info = "";
@@ -183,11 +183,11 @@ describe("standalone context", () => {
 		expect(clicks).toBe(0);
 	});
 
-	it("i dispatches sw-info with the content and the anchor element", async () => {
+	it("i dispatches yk-info with the content and the anchor element", async () => {
 		const el = await standalone({ label: "dock", info: "requests docking clearance" });
-		const got: SwInfoDetail[] = [];
-		document.body.addEventListener("sw-info", (e) => {
-			got.push((e as CustomEvent<SwInfoDetail>).detail);
+		const got: YkInfoDetail[] = [];
+		document.body.addEventListener("yk-info", (e) => {
+			got.push((e as CustomEvent<YkInfoDetail>).detail);
 		});
 		inner(el).dispatchEvent(
 			new KeyboardEvent("keydown", { key: "i", bubbles: true, composed: true, cancelable: true }),
@@ -198,7 +198,7 @@ describe("standalone context", () => {
 	it("i without info does nothing", async () => {
 		const el = await standalone({ label: "dock" });
 		let infos = 0;
-		document.body.addEventListener("sw-info", () => {
+		document.body.addEventListener("yk-info", () => {
 			infos += 1;
 		});
 		const e = new KeyboardEvent("keydown", {
@@ -214,7 +214,7 @@ describe("standalone context", () => {
 });
 
 // Activation feedback: the held look under a pointer, and the flash that
-// acknowledges every successful activation for --sw-press-ms (press-flash.ts).
+// acknowledges every successful activation for --yk-press-ms (press-flash.ts).
 // The classes land on the HOST element: jsdom computes custom properties on
 // light-DOM elements but not through shadow boundaries, and the host is also
 // where authors can observe the state from outside.
@@ -229,14 +229,14 @@ describe("activation feedback", () => {
 	const pointer = (type: string, init: PointerEventInit = {}): PointerEvent =>
 		new PointerEvent(type, { bubbles: true, composed: true, button: 0, ...init });
 
-	it("flashes on activation and clears after --sw-press-ms", async () => {
+	it("flashes on activation and clears after --yk-press-ms", async () => {
 		const el = await standalone({ label: "go" });
 		el.click();
-		expect(el.classList.contains("sw-pressed")).toBe(true);
+		expect(el.classList.contains("yk-pressed")).toBe(true);
 		vi.advanceTimersByTime(PRESS_MS - 1);
-		expect(el.classList.contains("sw-pressed")).toBe(true);
+		expect(el.classList.contains("yk-pressed")).toBe(true);
 		vi.advanceTimersByTime(1);
-		expect(el.classList.contains("sw-pressed")).toBe(false);
+		expect(el.classList.contains("yk-pressed")).toBe(false);
 	});
 
 	it("re-arms the flash on rapid re-activation (no early cutoff)", async () => {
@@ -245,52 +245,52 @@ describe("activation feedback", () => {
 		vi.advanceTimersByTime(PRESS_MS / 2);
 		el.click(); // second activation, half way through the first flash
 		vi.advanceTimersByTime(PRESS_MS / 2); // the first flash's timer would have fired here
-		expect(el.classList.contains("sw-pressed")).toBe(true);
+		expect(el.classList.contains("yk-pressed")).toBe(true);
 		vi.advanceTimersByTime(PRESS_MS / 2);
-		expect(el.classList.contains("sw-pressed")).toBe(false);
+		expect(el.classList.contains("yk-pressed")).toBe(false);
 	});
 
 	it("reads the flash duration from the live cascade (the accessibility knob)", async () => {
 		const el = await standalone({ label: "go" });
-		el.style.setProperty("--sw-press-ms", "50ms");
+		el.style.setProperty("--yk-press-ms", "50ms");
 		el.click();
 		vi.advanceTimersByTime(49);
-		expect(el.classList.contains("sw-pressed")).toBe(true);
+		expect(el.classList.contains("yk-pressed")).toBe(true);
 		vi.advanceTimersByTime(1);
-		expect(el.classList.contains("sw-pressed")).toBe(false);
+		expect(el.classList.contains("yk-pressed")).toBe(false);
 	});
 
 	it("holds while the pointer is down, releases on pointerup", async () => {
 		const el = await standalone({ label: "go" });
 		inner(el).dispatchEvent(pointer("pointerdown"));
-		expect(el.classList.contains("sw-held")).toBe(true);
+		expect(el.classList.contains("yk-held")).toBe(true);
 		inner(el).dispatchEvent(pointer("pointerup"));
-		expect(el.classList.contains("sw-held")).toBe(false);
+		expect(el.classList.contains("yk-held")).toBe(false);
 	});
 
 	it("releases the hold when the pointer leaves or is cancelled", async () => {
 		const el = await standalone({ label: "go" });
 		inner(el).dispatchEvent(pointer("pointerdown"));
 		el.dispatchEvent(new PointerEvent("pointerleave")); // leave does not bubble; it fires on the host itself
-		expect(el.classList.contains("sw-held")).toBe(false);
+		expect(el.classList.contains("yk-held")).toBe(false);
 		inner(el).dispatchEvent(pointer("pointerdown"));
 		inner(el).dispatchEvent(pointer("pointercancel"));
-		expect(el.classList.contains("sw-held")).toBe(false);
+		expect(el.classList.contains("yk-held")).toBe(false);
 	});
 
 	it("only the primary button holds", async () => {
 		const el = await standalone({ label: "go" });
 		inner(el).dispatchEvent(pointer("pointerdown", { button: 2 }));
-		expect(el.classList.contains("sw-held")).toBe(false);
+		expect(el.classList.contains("yk-held")).toBe(false);
 	});
 
 	it("disabled: neither holds nor flashes (nothing happened; nothing must claim otherwise)", async () => {
 		const el = await standalone({ label: "go", disabled: true });
 		inner(el).dispatchEvent(pointer("pointerdown"));
-		expect(el.classList.contains("sw-held")).toBe(false);
+		expect(el.classList.contains("yk-held")).toBe(false);
 		inner(el).click(); // dies in the shadow-root suppressor
 		el.click(); // the direct host path the suppressor never sees
-		expect(el.classList.contains("sw-pressed")).toBe(false);
+		expect(el.classList.contains("yk-pressed")).toBe(false);
 	});
 });
 
@@ -355,8 +355,8 @@ describe("disabled state", () => {
 		el.addEventListener("click", () => {
 			clicks += 1;
 		});
-		document.body.addEventListener("sw-info", (e) => {
-			infos.push((e as CustomEvent<SwInfoDetail>).detail.content);
+		document.body.addEventListener("yk-info", (e) => {
+			infos.push((e as CustomEvent<YkInfoDetail>).detail.content);
 		});
 		inner(el).dispatchEvent(
 			new KeyboardEvent("keydown", { key: "d", bubbles: true, composed: true, cancelable: true }),
@@ -370,13 +370,13 @@ describe("disabled state", () => {
 });
 
 // Row mode, driven through a real controller: the same rig pattern as
-// megacolumn-controller.test.ts, except rows carry live sw-button elements.
+// megacolumn-controller.test.ts, except rows carry live yk-button elements.
 describe("inside a megacolumn row", () => {
 	interface Rig {
 		container: HTMLElement;
 		ctl: MegacolumnController;
 		key: (key: string) => KeyboardEvent;
-		button: (sel: string) => SwButton;
+		button: (sel: string) => YkButton;
 	}
 
 	async function makeRig(innerHtml: string): Promise<Rig> {
@@ -385,8 +385,8 @@ describe("inside a megacolumn row", () => {
 		root.innerHTML = `<div data-mc-container>${innerHtml}</div>`;
 		const container = root.querySelector<HTMLElement>("[data-mc-container]");
 		if (container === null) throw new Error("unreachable: just rendered");
-		// Let the sw-buttons complete their first render before the scan.
-		await Promise.all([...root.querySelectorAll("sw-button")].map((b) => b.updateComplete));
+		// Let the yk-buttons complete their first render before the scan.
+		await Promise.all([...root.querySelectorAll("yk-button")].map((b) => b.updateComplete));
 		const ctl = new MegacolumnController({
 			renderRoot: root,
 			addController(): void {},
@@ -400,9 +400,9 @@ describe("inside a megacolumn row", () => {
 			container.dispatchEvent(e);
 			return e;
 		};
-		const button = (sel: string): SwButton => {
-			const b = root.querySelector<SwButton>(sel);
-			if (b === null) throw new Error(`no sw-button at ${sel}`);
+		const button = (sel: string): YkButton => {
+			const b = root.querySelector<YkButton>(sel);
+			if (b === null) throw new Error(`no yk-button at ${sel}`);
 			return b;
 		};
 		return { container, ctl, key, button };
@@ -410,9 +410,9 @@ describe("inside a megacolumn row", () => {
 
 	it("renders non-focusable markup: the controller's tab-stop scan accepts it", async () => {
 		const { button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="alpha"></sw-button></div>
+			<div data-mc-row="a"><yk-button label="alpha"></yk-button></div>
 		`);
-		expect(inner(button("sw-button")).tagName).toBe("DIV"); // no native button in row mode
+		expect(inner(button("yk-button")).tagName).toBe("DIV"); // no native button in row mode
 	});
 
 	it("throws when loose in a container outside any row", async () => {
@@ -428,17 +428,17 @@ describe("inside a megacolumn row", () => {
 		window.addEventListener("error", onError);
 		const root = document.createElement("div");
 		document.body.replaceChildren(root);
-		root.innerHTML = `<div data-mc-container><sw-button label="stray"></sw-button></div>`;
+		root.innerHTML = `<div data-mc-container><yk-button label="stray"></yk-button></div>`;
 		window.removeEventListener("error", onError);
 		expect(reported.some((m) => /outside any row/.test(m))).toBe(true);
 	});
 
 	it("Enter and Space at its position activate as a click", async () => {
 		const { key, button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="alpha"></sw-button></div>
+			<div data-mc-row="a"><yk-button label="alpha"></yk-button></div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		expect(key("Enter").defaultPrevented).toBe(true);
@@ -448,18 +448,18 @@ describe("inside a megacolumn row", () => {
 
 	it("a mouse click activates exactly once (the pointer-activation dedup)", async () => {
 		// The controller synthesizes a megacolumn-key Enter from unclaimed clicks on
-		// row controls; sw-button activates on its own native click and CLAIMS it
+		// row controls; yk-button activates on its own native click and CLAIMS it
 		// (preventDefault, the same idiom as claiming a forwarded key), so the
 		// synthesis skips it and the author's @click fires once, not twice.
 		const { button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="alpha"></sw-button></div>
+			<div data-mc-row="a"><yk-button label="alpha"></yk-button></div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		const e = new MouseEvent("click", { bubbles: true, cancelable: true, composed: true });
-		inner(button("sw-button")).dispatchEvent(e);
+		inner(button("yk-button")).dispatchEvent(e);
 		expect(clicks).toBe(1);
 		expect(e.defaultPrevented).toBe(true); // claimed: the controller must not synthesize
 	});
@@ -477,11 +477,11 @@ describe("inside a megacolumn row", () => {
 		// row-mode claim marks it, so the controller must not synthesize a second
 		// Enter from the keyboard's own acknowledgment click.
 		const { container, button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="alpha"></sw-button></div>
+			<div data-mc-row="a"><yk-button label="alpha"></yk-button></div>
 		`);
 		let clicks = 0;
 		let mcKeys = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		container.parentElement?.addEventListener("megacolumn-key", () => {
@@ -498,10 +498,10 @@ describe("inside a megacolumn row", () => {
 		// The see-before-commit rhythm: the letter is a jump, Enter is the commit.
 		const { ctl, key, button } = await makeRig(`
 			<div data-mc-row="a">plain row</div>
-			<div data-mc-row="dock"><sw-button label="dock" hotkey="d"></sw-button></div>
+			<div data-mc-row="dock"><yk-button label="dock" hotkey="d"></yk-button></div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		const e = key("d"); // focus is on row a
@@ -515,11 +515,11 @@ describe("inside a megacolumn row", () => {
 	it("its own hotkey while focused does nothing (Enter/Space are the activation)", async () => {
 		// Double-tap must not out-power single-tap: the letter only ever jumps.
 		const { ctl, key, button } = await makeRig(`
-			<div data-mc-row="dock"><sw-button label="dock" hotkey="d"></sw-button></div>
+			<div data-mc-row="dock"><yk-button label="dock" hotkey="d"></yk-button></div>
 			<div data-mc-row="b">plain row</div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		const e = key("d"); // focus starts on the dock row itself
@@ -531,10 +531,10 @@ describe("inside a megacolumn row", () => {
 	it("hotkey-direct: the letter activates immediately, with focus moved to this row", async () => {
 		const { ctl, key, button } = await makeRig(`
 			<div data-mc-row="a">plain row</div>
-			<div data-mc-row="dock"><sw-button label="dock" hotkey="d" hotkey-direct></sw-button></div>
+			<div data-mc-row="dock"><yk-button label="dock" hotkey="d" hotkey-direct></yk-button></div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		const e = key("d"); // focus is on row a
@@ -549,11 +549,11 @@ describe("inside a megacolumn row", () => {
 		// A direct letter has the same power from every row, its own included:
 		// there is no jump for it to mean instead.
 		const { key, button } = await makeRig(`
-			<div data-mc-row="dock"><sw-button label="dock" hotkey="d" hotkey-direct></sw-button></div>
+			<div data-mc-row="dock"><yk-button label="dock" hotkey="d" hotkey-direct></yk-button></div>
 			<div data-mc-row="b">plain row</div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		expect(key("d").defaultPrevented).toBe(true); // focus starts on the dock row
@@ -564,13 +564,13 @@ describe("inside a megacolumn row", () => {
 		const { ctl, key, button } = await makeRig(`
 			<div data-mc-row="crate">
 				<span data-mc-main>ore crate</span>
-				<span data-mc-aux><sw-button label="×"></sw-button></span>
+				<span data-mc-aux><yk-button label="×"></yk-button></span>
 			</div>
 			<div data-mc-row="plain">plain row</div>
-			<div data-mc-row="wipe"><sw-button label="wipe all" hotkey="x"></sw-button></div>
+			<div data-mc-row="wipe"><yk-button label="wipe all" hotkey="x"></yk-button></div>
 		`);
 		let auxClicks = 0;
-		button("[data-mc-aux] sw-button").addEventListener("click", () => {
+		button("[data-mc-aux] yk-button").addEventListener("click", () => {
 			auxClicks += 1;
 		});
 		key("x"); // focus on the crate row: its aux claims first, the marking never hears
@@ -584,13 +584,13 @@ describe("inside a megacolumn row", () => {
 
 	it("an i-marked button yields to a focused button's own info (the popup norm survives)", async () => {
 		const { ctl, key } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="dock" info="requests clearance"></sw-button></div>
+			<div data-mc-row="a"><yk-button label="dock" info="requests clearance"></yk-button></div>
 			<div data-mc-row="plain">plain row</div>
-			<div data-mc-row="atlas"><sw-button label="atlas" hotkey="i"></sw-button></div>
+			<div data-mc-row="atlas"><yk-button label="atlas" hotkey="i"></yk-button></div>
 		`);
 		const infos: string[] = [];
-		document.body.addEventListener("sw-info", (e) => {
-			infos.push((e as CustomEvent<SwInfoDetail>).detail.content);
+		document.body.addEventListener("yk-info", (e) => {
+			infos.push((e as CustomEvent<YkInfoDetail>).detail.content);
 		});
 		key("i"); // focus on the dock row: its own info claims i, the marking never hears
 		expect(infos).toEqual(["requests clearance"]);
@@ -601,24 +601,24 @@ describe("inside a megacolumn row", () => {
 		expect(infos).toEqual(["requests clearance"]); // a jump, not the info norm
 	});
 
-	it("duplicate sw-button hotkeys in one column throw at scan time", async () => {
+	it("duplicate yk-button hotkeys in one column throw at scan time", async () => {
 		await expect(
 			makeRig(`
-				<div data-mc-row="a"><sw-button label="dock" hotkey="d"></sw-button></div>
-				<div data-mc-row="b"><sw-button label="drift" hotkey="d"></sw-button></div>
+				<div data-mc-row="a"><yk-button label="dock" hotkey="d"></yk-button></div>
+				<div data-mc-row="b"><yk-button label="drift" hotkey="d"></yk-button></div>
 			`),
 		).rejects.toThrow(/duplicate hotkey marking "d"/);
 	});
 
-	it("an aux sw-button answers x and Delete from anywhere in the row", async () => {
+	it("an aux yk-button answers x and Delete from anywhere in the row", async () => {
 		const { key, button } = await makeRig(`
 			<div data-mc-row="crate">
 				<span data-mc-main>ore crate</span>
-				<span data-mc-aux><sw-button label="×"></sw-button></span>
+				<span data-mc-aux><yk-button label="×"></yk-button></span>
 			</div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		expect(key("x").defaultPrevented).toBe(true); // focus is at main position
@@ -626,12 +626,12 @@ describe("inside a megacolumn row", () => {
 		expect(clicks).toBe(2);
 	});
 
-	it("a main sw-button does NOT answer x (removal is the aux norm)", async () => {
+	it("a main yk-button does NOT answer x (removal is the aux norm)", async () => {
 		const { key, button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="alpha"></sw-button></div>
+			<div data-mc-row="a"><yk-button label="alpha"></yk-button></div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		key("x");
@@ -642,11 +642,11 @@ describe("inside a megacolumn row", () => {
 		const { key, button } = await makeRig(`
 			<div data-mc-row="crate">
 				<span data-mc-main>ore crate</span>
-				<span data-mc-aux><sw-button label="×"></sw-button></span>
+				<span data-mc-aux><yk-button label="×"></yk-button></span>
 			</div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		key("Enter"); // main position: the aux button must stay quiet
@@ -656,28 +656,28 @@ describe("inside a megacolumn row", () => {
 		expect(clicks).toBe(1);
 	});
 
-	it("i at its position dispatches sw-info", async () => {
+	it("i at its position dispatches yk-info", async () => {
 		const { key, button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="dock" info="requests clearance"></sw-button></div>
+			<div data-mc-row="a"><yk-button label="dock" info="requests clearance"></yk-button></div>
 		`);
-		const got: SwInfoDetail[] = [];
-		document.body.addEventListener("sw-info", (e) => {
-			got.push((e as CustomEvent<SwInfoDetail>).detail);
+		const got: YkInfoDetail[] = [];
+		document.body.addEventListener("yk-info", (e) => {
+			got.push((e as CustomEvent<YkInfoDetail>).detail);
 		});
 		const e = key("i");
 		expect(got.length).toBe(1);
 		expect(got[0]?.content).toBe("requests clearance");
-		expect(got[0]?.source).toBe(button("sw-button"));
+		expect(got[0]?.source).toBe(button("yk-button"));
 		expect(e.defaultPrevented).toBe(true);
 	});
 
 	it("disabled: claims its keys but does not act (Space must not fall through)", async () => {
 		const { ctl, key, button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="dock" hotkey="d" hotkey-direct disabled></sw-button></div>
+			<div data-mc-row="a"><yk-button label="dock" hotkey="d" hotkey-direct disabled></yk-button></div>
 			<div data-mc-row="b">plain row</div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		expect(key(" ").defaultPrevented).toBe(true); // claimed...
@@ -692,11 +692,11 @@ describe("inside a megacolumn row", () => {
 
 	it("disabled, non-direct: its hotkey still jumps focus to it (i can ask why)", async () => {
 		const { ctl, key, button } = await makeRig(`
-			<div data-mc-row="a"><sw-button label="dock" hotkey="d" disabled></sw-button></div>
+			<div data-mc-row="a"><yk-button label="dock" hotkey="d" disabled></yk-button></div>
 			<div data-mc-row="b">plain row</div>
 		`);
 		let clicks = 0;
-		button("sw-button").addEventListener("click", () => {
+		button("yk-button").addEventListener("click", () => {
 			clicks += 1;
 		});
 		key("ArrowDown"); // -> b
@@ -710,16 +710,16 @@ describe("inside a megacolumn row", () => {
 	it("two buttons in a row each claim only their own position's Enter", async () => {
 		const { key, button } = await makeRig(`
 			<div data-mc-row="pair">
-				<span data-mc-main><sw-button label="inspect"></sw-button></span>
-				<span data-mc-aux><sw-button label="×"></sw-button></span>
+				<span data-mc-main><yk-button label="inspect"></yk-button></span>
+				<span data-mc-aux><yk-button label="×"></yk-button></span>
 			</div>
 		`);
 		let mains = 0;
 		let auxes = 0;
-		button("[data-mc-main] sw-button").addEventListener("click", () => {
+		button("[data-mc-main] yk-button").addEventListener("click", () => {
 			mains += 1;
 		});
-		button("[data-mc-aux] sw-button").addEventListener("click", () => {
+		button("[data-mc-aux] yk-button").addEventListener("click", () => {
 			auxes += 1;
 		});
 		key("Enter");
diff --git a/packages/yukey/src/yk-button.ts b/packages/yukey/src/yk-button.ts
index 1eda933..89dcd1c 100644
--- a/packages/yukey/src/yk-button.ts
+++ b/packages/yukey/src/yk-button.ts
@@ -1,21 +1,21 @@
-// sw-button: the button that makes the guidelines' keyboard norms structural.
+// yk-button: the button that makes the guidelines' keyboard norms structural.
 // Use it and the norms happen; there is nothing to remember per screen.
 //
 // The norms it embodies (docs/precepts-we-love-the-keyboard.md, "buttons always show their hotkey"
 // and the megacolumn sections):
 //
 //   - Hotkey marking: if the hotkey letter occurs in the label, its first
-//     occurrence renders in --sw-hotkey-accent; otherwise the letter shows in a
+//     occurrence renders in --yk-hotkey-accent; otherwise the letter shows in a
 //     slot 70% of the way across.  A complex button (more than one concept of
 //     activation) sets `icon` instead, which claims that slot: a single letter
 //     would mislead, so the icon says "more here; ask i".
 //   - The i norm: pressing i (forwarded in a row, or while focused standalone)
-//     dispatches a composed `sw-info` event carrying this button's `info` text.
-//     sw-infopop renders the popup; `info` is also mirrored onto data-info,
-//     the attribute sw-infopop's hover trigger finds carriers by
+//     dispatches a composed `yk-info` event carrying this button's `info` text.
+//     yk-infopop renders the popup; `info` is also mirrored onto data-info,
+//     the attribute yk-infopop's hover trigger finds carriers by
 //     (one authored content, two triggers).
 //     i answers even when disabled: it is how you ask why a thing is off.
-//   - Hit targets from --sw-hit-min-h/w.
+//   - Hit targets from --yk-hit-min-h/w.
 //   - Active (`active`): the in-effect option among sibling choices, marked with
 //     the selected wash and aria-current -- and STILL clickable: re-issuing an
 //     in-effect choice is the norm's contract (owners decide whether re-issue
@@ -46,7 +46,7 @@
 //                                                      and Enter/Space then activate
 //                                                      (see-before-commit).
 //       x/Delete from any position, aux buttons only-> activate (the removal norm)
-//       i at this button's own position             -> sw-info
+//       i at this button's own position             -> yk-info
 //   - Standalone (no [data-mc-container] ancestor): a real native button; Tab
 //     reaches it, Enter/Space activate natively, the focus ring is the tokens'.
 //     Its hotkey and i answer only while it holds focus (a broader standalone
@@ -66,10 +66,10 @@
 // click default is not this component's to consume.
 //
 // Activation feedback: every successful activation flashes the pressed look for
-// --sw-press-ms (press-flash.ts), so keyboard triggers are as visible as a mouse
+// --yk-press-ms (press-flash.ts), so keyboard triggers are as visible as a mouse
 // press; a pointer held down shows the same look for as long as it stays down.
 // Disabled buttons show neither: nothing happened, nothing must claim otherwise.
-// The sw-pressed/sw-held classes live on the HOST element, outside classMap's
+// The yk-pressed/yk-held classes live on the HOST element, outside classMap's
 // managed set, so renders never clobber a flash in flight.
 
 import { css, html, LitElement, type TemplateResult } from "lit";
@@ -78,10 +78,10 @@ import { classMap } from "lit/directives/class-map.js";
 import { ifDefined } from "lit/directives/if-defined.js";
 import type { MegacolumnKeyDetail } from "./megacolumn-controller.js";
 import { flashPress } from "./press-flash.js";
-import { dispatchSwInfo } from "./yk-info.js";
+import { dispatchYkInfo } from "./yk-info.js";
 
-@customElement("sw-button")
-export class SwButton extends LitElement {
+@customElement("yk-button")
+export class YkButton extends LitElement {
 	static styles = css`
 		:host {
 			display: inline-flex;
@@ -92,33 +92,33 @@ export class SwButton extends LitElement {
 			align-items: center;
 			justify-content: center;
 			flex: 1;
-			min-height: var(--sw-hit-min-h);
-			min-width: var(--sw-hit-min-w);
-			padding: 2px var(--sw-space-md);
+			min-height: var(--yk-hit-min-h);
+			min-width: var(--yk-hit-min-w);
+			padding: 2px var(--yk-space-md);
 			box-sizing: border-box;
 			background: none;
-			border: 1px solid var(--sw-border);
-			color: var(--sw-text);
+			border: 1px solid var(--yk-border);
+			color: var(--yk-text);
 			font: inherit;
 			cursor: pointer;
 			user-select: none;
 			white-space: nowrap;
 		}
 		.btn:hover {
-			color: var(--sw-text-bright);
-			border-color: var(--sw-border-bright);
+			color: var(--yk-text-bright);
+			border-color: var(--yk-border-bright);
 		}
 		button.btn:focus-visible {
-			outline: var(--sw-focus-ring);
-			outline-offset: var(--sw-focus-ring-offset);
+			outline: var(--yk-focus-ring);
+			outline-offset: var(--yk-focus-ring-offset);
 		}
 		.hk,
 		.slot {
-			color: var(--sw-hotkey-accent);
+			color: var(--yk-hotkey-accent);
 		}
 		/* the 70%-offset hotkey slot, per the guidelines' button-marking norm */
 		.btn.slotted {
-			padding-right: calc(var(--sw-space-xxl) + var(--sw-space-md));
+			padding-right: calc(var(--yk-space-xxl) + var(--yk-space-md));
 		}
 		.slot {
 			position: absolute;
@@ -128,23 +128,23 @@ export class SwButton extends LitElement {
 		}
 		/* the aux (small-x) form: compact, danger-tinted, borderless like the norm */
 		.btn.aux {
-			color: var(--sw-danger);
+			color: var(--yk-danger);
 			border-color: transparent;
 			padding: 0;
 			flex: none;
 		}
 		.btn.aux:hover {
-			border-color: var(--sw-danger-border);
+			border-color: var(--yk-danger-border);
 		}
 		.btn.disabled {
-			color: var(--sw-text-faint);
-			border-color: var(--sw-border-dim);
+			color: var(--yk-text-faint);
+			border-color: var(--yk-border-dim);
 			cursor: default;
 		}
 		/* the active (in-effect) option: selected wash, brighter text, still clickable */
 		.btn.active {
-			color: var(--sw-text-bright);
-			background: var(--sw-wash-selected);
+			color: var(--yk-text-bright);
+			background: var(--yk-wash-selected);
 		}
 		/* a dead hotkey must not advertise itself in the significant color */
 		.btn.disabled .hk,
@@ -153,11 +153,11 @@ export class SwButton extends LitElement {
 		}
 		/* activation feedback: the same look for held-under-a-pointer and for the
 		   post-activation flash (see the file header) */
-		:host(.sw-held) .btn,
-		:host(.sw-pressed) .btn {
-			background: var(--sw-wash-pressed);
-			border-color: var(--sw-accent-bright);
-			color: var(--sw-text-bright);
+		:host(.yk-held) .btn,
+		:host(.yk-pressed) .btn {
+			background: var(--yk-wash-pressed);
+			border-color: var(--yk-accent-bright);
+			color: var(--yk-text-bright);
 		}
 	`;
 
@@ -171,7 +171,7 @@ export class SwButton extends LitElement {
 	active = false;
 
 	// The info content, mirrored synchronously onto data-info: that attribute is
-	// the carrier contract sw-infopop's hover trigger finds elements by
+	// the carrier contract yk-infopop's hover trigger finds elements by
 	// (decision-info-content: one authored content, two triggers), and mirroring
 	// keeps this property a typed accessor over the same single source.
 	#info = "";
@@ -198,7 +198,7 @@ export class SwButton extends LitElement {
 	}
 	set hotkey(v: string) {
 		if (v !== "" && !/^[a-z0-9]$/i.test(v)) {
-			throw new Error(`sw-button: hotkey "${v}" must be a single [a-z0-9] character`);
+			throw new Error(`yk-button: hotkey "${v}" must be a single [a-z0-9] character`);
 		}
 		const norm = v.toLowerCase();
 		this.#hotkey = norm;
@@ -233,7 +233,7 @@ export class SwButton extends LitElement {
 		this.#row = this.closest("[data-mc-row]");
 		if (this.#row === null && this.closest("[data-mc-container]") !== null) {
 			throw new Error(
-				"sw-button: inside a megacolumn container but outside any row; "
+				"yk-button: inside a megacolumn container but outside any row; "
 					+ "a loose tab stop would fracture the single-tab-stop model",
 			);
 		}
@@ -319,11 +319,11 @@ export class SwButton extends LitElement {
 
 	#onPointerDown = (e: PointerEvent): void => {
 		if (e.button !== 0 || this.disabled) return;
-		this.classList.add("sw-held");
+		this.classList.add("yk-held");
 	};
 
 	#releaseHold = (): void => {
-		this.classList.remove("sw-held");
+		this.classList.remove("yk-held");
 	};
 
 	// A claimed key is consumed whether or not the button acts: a disabled
@@ -335,7 +335,7 @@ export class SwButton extends LitElement {
 	}
 
 	#dispatchInfo(): void {
-		dispatchSwInfo(this, this.info);
+		dispatchYkInfo(this, this.info);
 	}
 
 	#position(): "main" | "aux" {
@@ -397,6 +397,6 @@ export class SwButton extends LitElement {
 
 declare global {
 	interface HTMLElementTagNameMap {
-		"sw-button": SwButton;
+		"yk-button": YkButton;
 	}
 }
diff --git a/packages/yukey/src/yk-info.ts b/packages/yukey/src/yk-info.ts
index d63cf3c..279a9c9 100644
--- a/packages/yukey/src/yk-info.ts
+++ b/packages/yukey/src/yk-info.ts
@@ -1,31 +1,31 @@
-// The info norm's wiring: the sw-info event shape and the data-info carrier contract.
+// The info norm's wiring: the yk-info event shape and the data-info carrier contract.
 // This file is the SSOT for both.  (The norm itself: docs/precepts-we-love-the-keyboard.md,
 // "info pops" -- i means "info, please" on almost any element.)
 //
 // One authored content, two triggers:
 //
-//   - `data-info="<content>"` on any element makes it a CARRIER.  sw-infopop's
+//   - `data-info="<content>"` on any element makes it a CARRIER.  yk-infopop's
 //     hover trigger finds carriers by this attribute, and the megacolumn
 //     controller answers otherwise-unclaimed i forwards for them.  Components
-//     with an info property (sw-button) mirror it onto this attribute;
+//     with an info property (yk-button) mirror it onto this attribute;
 //     plain markup writes the attribute directly.  Empty means not a carrier.
-//   - `sw-info` (bubbles + composed) is the keyboard trigger's event: a producer
-//     that answered i dispatches it, sw-infopop renders it.  The detail carries
+//   - `yk-info` (bubbles + composed) is the keyboard trigger's event: a producer
+//     that answered i dispatches it, yk-infopop renders it.  The detail carries
 //     the anchor element because composed events retarget: a listener outside
 //     the shadow tree sees only the host chain, never the real source.
 //
-// Producers today: sw-button (its own i claim, standalone and in rows) and
+// Producers today: yk-button (its own i claim, standalone and in rows) and
 // MegacolumnController (the fallback for plain [data-info] rows).
 
-export interface SwInfoDetail {
+export interface YkInfoDetail {
 	content: string;
 	// The element to anchor the popup to (see the retargeting note above).
 	source: HTMLElement;
 }
 
-export function dispatchSwInfo(source: HTMLElement, content: string): void {
+export function dispatchYkInfo(source: HTMLElement, content: string): void {
 	source.dispatchEvent(
-		new CustomEvent<SwInfoDetail>("sw-info", {
+		new CustomEvent<YkInfoDetail>("yk-info", {
 			detail: { content, source },
 			bubbles: true,
 			composed: true,
diff --git a/packages/yukey/src/yk-infopop.gallery.ts b/packages/yukey/src/yk-infopop.gallery.ts
index 67a0294..6ba8abf 100644
--- a/packages/yukey/src/yk-infopop.gallery.ts
+++ b/packages/yukey/src/yk-infopop.gallery.ts
@@ -1,4 +1,4 @@
-// Gallery entry for sw-infopop: keyboard i and mouse hover raising the same
+// Gallery entry for yk-infopop: keyboard i and mouse hover raising the same
 // authored content, the pin precedence, plain-row participation via data-info,
 // and the edge flip.  The popup instance under demonstration is the gallery
 // shell's own -- one per app is the model, so the demos raise that one.
@@ -9,11 +9,11 @@ import type { GalleryEntry } from "./gallery/entry.js";
 import { MegacolumnController, type MegacolumnKeyDetail, megacolumnStyles } from "./megacolumn-controller.js";
 import "./yk-button.js";
 
-// A megacolumn where every row flavor carries info: sw-buttons answer i
+// A megacolumn where every row flavor carries info: yk-buttons answer i
 // themselves; the plain text main, the slider row, and the aux x are answered
 // by the controller's [data-info] fallback.  Hover hits the same attributes.
-@customElement("swg-infopop-column")
-export class SwgInfopopColumn extends LitElement {
+@customElement("ykg-infopop-column")
+export class YkgInfopopColumn extends LitElement {
 	static styles = [
 		megacolumnStyles,
 		css`
@@ -21,17 +21,17 @@ export class SwgInfopopColumn extends LitElement {
 				display: block;
 			}
 			[data-mc-container] {
-				border: 1px solid var(--sw-border-dim);
-				padding: var(--sw-space-md);
+				border: 1px solid var(--yk-border-dim);
+				padding: var(--yk-space-md);
 			}
 			[data-mc-row] {
 				display: flex;
 				align-items: center;
-				gap: var(--sw-space-sm);
-				min-height: var(--sw-hit-min-h);
-				padding: 2px var(--sw-space-sm);
+				gap: var(--yk-space-sm);
+				min-height: var(--yk-hit-min-h);
+				padding: 2px var(--yk-space-sm);
 			}
-			[data-mc-row] sw-button:first-child {
+			[data-mc-row] yk-button:first-child {
 				flex: 1;
 			}
 			.grow {
@@ -40,31 +40,31 @@ export class SwgInfopopColumn extends LitElement {
 			.meter {
 				flex: 1;
 				height: 8px;
-				border: 1px solid var(--sw-border-dim);
+				border: 1px solid var(--yk-border-dim);
 				position: relative;
 			}
 			.meter .fill {
 				position: absolute;
 				inset: 0 auto 0 0;
-				background: var(--sw-accent);
+				background: var(--yk-accent);
 			}
 			.pct {
-				color: var(--sw-text-hi);
+				color: var(--yk-text-hi);
 				width: 4ch;
 				text-align: right;
 			}
 			[data-mc-aux] {
-				color: var(--sw-danger);
-				min-width: var(--sw-hit-min-w);
-				min-height: var(--sw-hit-min-h);
+				color: var(--yk-danger);
+				min-width: var(--yk-hit-min-w);
+				min-height: var(--yk-hit-min-h);
 				display: inline-flex;
 				align-items: center;
 				justify-content: center;
 			}
 			.log {
-				margin-top: var(--sw-space-md);
-				color: var(--sw-good);
-				font-size: var(--sw-type-ui);
+				margin-top: var(--yk-space-md);
+				color: var(--yk-good);
+				font-size: var(--yk-type-ui);
 				min-height: 1.4em;
 			}
 		`,
@@ -105,14 +105,14 @@ export class SwgInfopopColumn extends LitElement {
 		return html`
 			<div data-mc-container aria-label=${this.label} @megacolumn-key=${this.onMegacolumnKey}>
 				<div data-mc-row="dock">
-					<sw-button
+					<yk-button
 						label="Dock at station"
 						hotkey="d"
-						info="an sw-button answers i itself; hover reads the same data-info mirror"
+						info="a yk-button answers i itself; hover reads the same data-info mirror"
 						@click=${() => {
 			this.last = "docked";
 		}}
-					></sw-button>
+					></yk-button>
 				</div>
 				<div data-mc-row="survey" data-info="a plain text row: the megacolumn answers i for its data-info">
 					<span class="grow">queue survey</span>
@@ -134,8 +134,8 @@ export class SwgInfopopColumn extends LitElement {
 
 // Buttons pushed against the stage edges, so the preferred below-centered
 // placement has to clamp (sides) or flip (bottom edge, when the page is short).
-@customElement("swg-infopop-edges")
-export class SwgInfopopEdges extends LitElement {
+@customElement("ykg-infopop-edges")
+export class YkgInfopopEdges extends LitElement {
 	static styles = css`
 		:host {
 			display: block;
@@ -149,22 +149,22 @@ export class SwgInfopopEdges extends LitElement {
 	render(): TemplateResult {
 		return html`
 			<div class="corners">
-				<sw-button
+				<yk-button
 					label="far left"
 					info="anchored at the viewport's left: the popup clamps instead of centering"
-				></sw-button>
-				<sw-button
+				></yk-button>
+				<yk-button
 					label="far right"
 					info="anchored at the viewport's right: the popup clamps the other way"
-				></sw-button>
+				></yk-button>
 			</div>
 		`;
 	}
 }
 
 const entry: GalleryEntry = {
-	id: "sw-infopop",
-	title: "sw-infopop",
+	id: "yk-infopop",
+	title: "yk-infopop",
 	description: "The info norm's popup: keyboard i and mouse hover raise the SAME authored "
 		+ "content (one data-info attribute per element, two triggers).  Keyboard-raised "
 		+ "popups pin until any key or click; hover popups follow the pointer and never "
@@ -173,20 +173,20 @@ const entry: GalleryEntry = {
 	variants: [
 		{
 			caption: "Every row flavor carries info.  Keyboard: focus the column, press i on each row -- "
-				+ "the sw-button answers itself, the plain rows are answered by the megacolumn's data-info "
+				+ "the yk-button answers itself, the plain rows are answered by the megacolumn's data-info "
 				+ "fallback, and on the probe row, right-arrow onto the × for its own story.  "
 				+ "i again on the same row dismisses (a toggle); i after moving to another row follows.  "
 				+ "Mouse: hover the same places and linger; the identical text appears.  "
 				+ "Precedence: press i, then wave the mouse over other rows -- the pinned popup holds "
 				+ "until a key or click dismisses it.  Navigation keys close the popup AND still navigate.",
-			render: () => html`<swg-infopop-column></swg-infopop-column>`,
+			render: () => html`<ykg-infopop-column></ykg-infopop-column>`,
 		},
 		{
 			caption: "Edge behavior: these buttons hug the stage's sides, so the below-centered placement "
 				+ "clamps horizontally to stay inside the viewport.  Hover or focus-and-i near a viewport "
 				+ "edge (shrink the window height to see the bottom flip: the popup jumps above the anchor).",
 			wide: true,
-			render: () => html`<swg-infopop-edges></swg-infopop-edges>`,
+			render: () => html`<ykg-infopop-edges></ykg-infopop-edges>`,
 		},
 	],
 };
diff --git a/packages/yukey/src/yk-infopop.test.ts b/packages/yukey/src/yk-infopop.test.ts
index 19171c9..b41373a 100644
--- a/packages/yukey/src/yk-infopop.test.ts
+++ b/packages/yukey/src/yk-infopop.test.ts
@@ -1,17 +1,17 @@
 // @vitest-environment jsdom
 //
-// Tests for sw-infopop (sw-infopop.ts): triggers, precedence, and dismissal.
+// Tests for yk-infopop (yk-infopop.ts): triggers, precedence, and dismissal.
 // Geometry is NOT tested here (jsdom does no layout; rects are all zero) --
 // the placement rule has its own pure tests in infopop-place.test.ts, and the
 // browser verification covers the real flip.  Hover timing runs on fake timers.
 
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
-import { dispatchSwInfo } from "./yk-info.js";
-import { HOVER_DELAY_MS, type SwInfopop } from "./yk-infopop.js";
+import { dispatchYkInfo } from "./yk-info.js";
+import { HOVER_DELAY_MS, type YkInfopop } from "./yk-infopop.js";
 import "./yk-infopop.js";
 
 interface Rig {
-	pop: SwInfopop;
+	pop: YkInfopop;
 	rowA: HTMLElement;
 	rowB: HTMLElement;
 	aux: HTMLElement;
@@ -27,7 +27,7 @@ async function makeRig(): Promise<Rig> {
 		<div id="rowB" data-info="beta's story">beta</div>
 		<div id="bare">no info here</div>
 	`;
-	const pop = document.createElement("sw-infopop");
+	const pop = document.createElement("yk-infopop");
 	document.body.append(pop);
 	await pop.updateComplete;
 	const get = (id: string): HTMLElement => {
@@ -55,7 +55,7 @@ function key(el: HTMLElement | Window, k: string): KeyboardEvent {
 	return e;
 }
 
-async function settle(pop: SwInfopop): Promise<void> {
+async function settle(pop: YkInfopop): Promise<void> {
 	await pop.updateComplete;
 }
 
@@ -68,10 +68,10 @@ afterEach(() => {
 	vi.useRealTimers();
 });
 
-describe("the keyboard trigger (sw-info)", () => {
+describe("the keyboard trigger (yk-info)", () => {
 	it("opens pinned with the event's content, anchored to its source", async () => {
 		const { pop, rowA } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		await settle(pop);
 		expect(pop.mode).toBe("pinned");
 		expect(pop.anchor).toBe(rowA);
@@ -80,10 +80,10 @@ describe("the keyboard trigger (sw-info)", () => {
 		expect(pop.hasAttribute("aria-hidden")).toBe(false);
 	});
 
-	it("a later sw-info replaces the popup (new anchor, new content)", async () => {
+	it("a later yk-info replaces the popup (new anchor, new content)", async () => {
 		const { pop, rowA, rowB } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
-		dispatchSwInfo(rowB, "beta's story");
+		dispatchYkInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowB, "beta's story");
 		await settle(pop);
 		expect(pop.anchor).toBe(rowB);
 		expect(pop.shadowRoot?.textContent).toContain("beta's story");
@@ -176,7 +176,7 @@ describe("the hover trigger", () => {
 		document.body.append(host);
 		const shell = host.attachShadow({ mode: "open" });
 		shell.innerHTML = `<div id="carrier" data-info="in-shell story">thing</div>`;
-		const pop = document.createElement("sw-infopop");
+		const pop = document.createElement("yk-infopop");
 		shell.append(pop);
 		await pop.updateComplete;
 		const carrier = shell.getElementById("carrier");
@@ -200,7 +200,7 @@ describe("the hover trigger", () => {
 describe("precedence: keyboard pins", () => {
 	it("hover never replaces a pinned popup", async () => {
 		const { pop, rowA, rowB } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		hover(rowB);
 		vi.advanceTimersByTime(HOVER_DELAY_MS * 2);
 		await settle(pop);
@@ -210,7 +210,7 @@ describe("precedence: keyboard pins", () => {
 
 	it("pointer movement never closes a pinned popup", async () => {
 		const { pop, rowA, bare } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		hover(bare);
 		await settle(pop);
 		expect(pop.mode).toBe("pinned");
@@ -220,7 +220,7 @@ describe("precedence: keyboard pins", () => {
 		// The pointer sits on a carrier while a pinned popup has precedence; when a
 		// key closes the pinned one, nothing should appear without a fresh action.
 		const { pop, rowA, rowB } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		hover(rowB);
 		vi.advanceTimersByTime(HOVER_DELAY_MS); // fires, sees pinned, skips
 		key(window, "Escape");
@@ -235,7 +235,7 @@ describe("precedence: keyboard pins", () => {
 		vi.advanceTimersByTime(HOVER_DELAY_MS);
 		await settle(pop);
 		expect(pop.mode).toBe("hover");
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		await settle(pop);
 		expect(pop.mode).toBe("pinned");
 		expect(pop.anchor).toBe(rowA);
@@ -245,7 +245,7 @@ describe("precedence: keyboard pins", () => {
 describe("dismissal", () => {
 	it("any keydown closes, and the key is never consumed", async () => {
 		const { pop, rowA } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		const e = key(rowA, "ArrowDown");
 		await settle(pop);
 		expect(pop.mode).toBe("closed");
@@ -254,7 +254,7 @@ describe("dismissal", () => {
 
 	it("Escape that closes the popup is CONSUMED (one Escape, one layer)", async () => {
 		const { pop, rowA } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		const e = key(rowA, "Escape");
 		await settle(pop);
 		expect(pop.mode).toBe("closed");
@@ -271,19 +271,19 @@ describe("dismissal", () => {
 
 	it("bare modifiers close nothing", async () => {
 		const { pop, rowA } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		key(rowA, "Shift");
 		await settle(pop);
 		expect(pop.mode).toBe("pinned");
 	});
 
 	it("one keypress can close the old popup and raise the next (capture order)", async () => {
-		// The i norm in action: the source's keydown handler dispatches sw-info,
+		// The i norm in action: the source's keydown handler dispatches yk-info,
 		// and the popup's window-capture close has already run by then.
 		const { pop, rowA, rowB } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		rowB.addEventListener("keydown", (e) => {
-			if (e.key === "i") dispatchSwInfo(rowB, "beta's story");
+			if (e.key === "i") dispatchYkInfo(rowB, "beta's story");
 		});
 		key(rowB, "i");
 		await settle(pop);
@@ -296,7 +296,7 @@ describe("dismissal", () => {
 		// element already anchoring the pinned popup: the user is asking it away.
 		const { pop, rowA } = await makeRig();
 		rowA.addEventListener("keydown", (e) => {
-			if (e.key === "i") dispatchSwInfo(rowA, "alpha's story");
+			if (e.key === "i") dispatchYkInfo(rowA, "alpha's story");
 		});
 		key(rowA, "i");
 		await settle(pop);
@@ -309,7 +309,7 @@ describe("dismissal", () => {
 	it("the toggle suppression is scoped to one keypress (i works again afterward)", async () => {
 		const { pop, rowA } = await makeRig();
 		rowA.addEventListener("keydown", (e) => {
-			if (e.key === "i") dispatchSwInfo(rowA, "alpha's story");
+			if (e.key === "i") dispatchYkInfo(rowA, "alpha's story");
 		});
 		key(rowA, "i");
 		key(rowA, "i"); // toggles off
@@ -321,12 +321,12 @@ describe("dismissal", () => {
 
 	it("i on a DIFFERENT anchor during the same keypress is never suppressed", async () => {
 		// Guards the follow behavior against the toggle: the capture close sees
-		// key i + pinned mode there too, but the arriving sw-info names another
+		// key i + pinned mode there too, but the arriving yk-info names another
 		// anchor, so it must open.
 		const { pop, rowA, rowB } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		rowB.addEventListener("keydown", (e) => {
-			if (e.key === "i") dispatchSwInfo(rowB, "beta's story");
+			if (e.key === "i") dispatchYkInfo(rowB, "beta's story");
 		});
 		key(rowB, "i");
 		await settle(pop);
@@ -338,7 +338,7 @@ describe("dismissal", () => {
 		// The keyboard upgrade of a hover popup: only PINNED popups toggle off.
 		const { pop, rowA } = await makeRig();
 		rowA.addEventListener("keydown", (e) => {
-			if (e.key === "i") dispatchSwInfo(rowA, "alpha's story");
+			if (e.key === "i") dispatchYkInfo(rowA, "alpha's story");
 		});
 		hover(rowA);
 		vi.advanceTimersByTime(HOVER_DELAY_MS);
@@ -352,7 +352,7 @@ describe("dismissal", () => {
 
 	it("pointerdown anywhere closes", async () => {
 		const { pop, rowA, bare } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		bare.dispatchEvent(new MouseEvent("pointerdown", { bubbles: true, composed: true }));
 		await settle(pop);
 		expect(pop.mode).toBe("closed");
@@ -360,7 +360,7 @@ describe("dismissal", () => {
 
 	it("scroll closes (anchor rects go stale)", async () => {
 		const { pop, rowA } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		document.dispatchEvent(new Event("scroll"));
 		await settle(pop);
 		expect(pop.mode).toBe("closed");
@@ -378,7 +378,7 @@ describe("announcement surface, never interaction", () => {
 
 	it("returns to aria-hidden when closed again", async () => {
 		const { pop, rowA } = await makeRig();
-		dispatchSwInfo(rowA, "alpha's story");
+		dispatchYkInfo(rowA, "alpha's story");
 		await settle(pop);
 		key(window, "Escape");
 		await settle(pop);
diff --git a/packages/yukey/src/yk-infopop.ts b/packages/yukey/src/yk-infopop.ts
index 744e9f2..e590710 100644
--- a/packages/yukey/src/yk-infopop.ts
+++ b/packages/yukey/src/yk-infopop.ts
@@ -1,22 +1,22 @@
-// sw-infopop: the info norm's popup surface -- the keyboard's tooltip and the
+// yk-infopop: the info norm's popup surface -- the keyboard's tooltip and the
 // mouse's tooltip as ONE element showing one authored content per anchor
-// (the wiring SSOT -- the sw-info event and the data-info carrier attribute --
-// is sw-info.ts; the norm is docs/precepts-we-love-the-keyboard.md "info pops").
+// (the wiring SSOT -- the yk-info event and the data-info carrier attribute --
+// is yk-info.ts; the norm is docs/precepts-we-love-the-keyboard.md "info pops").
 //
 // One per app: place it once in the app shell's render (the gallery shell hosts
-// one today; sw-app hosts one at migration), and every sw-info event and every
+// one today; yk-app hosts one at migration), and every yk-info event and every
 // [data-info] hover in that tree raises it.  Two triggers:
 //
-//   - Keyboard: an sw-info event (composed, so it bubbles here from any open
+//   - Keyboard: a yk-info event (composed, so it bubbles here from any open
 //     shadow root below) opens the popup PINNED: pointer movement never closes
 //     or replaces it.  Any later keydown closes it -- heard in capture phase,
 //     so the very key that closes an old popup can raise the next one (i on
 //     another row closes-then-reopens), which is how the popup "follows" row
 //     focus without needing focus-change events.  One exception to re-raising:
 //     i on the anchor the pinned popup already shows is a TOGGLE -- the close
-//     notes which anchor that keypress dismissed, and the matching sw-info
+//     notes which anchor that keypress dismissed, and the matching yk-info
 //     arriving in the same dispatch is swallowed, so the second i dismisses
-//     instead of blinking closed-then-open.  (A different anchor's sw-info,
+//     instead of blinking closed-then-open.  (A different anchor's yk-info,
 //     or any i once that keypress ends, opens normally; an unpinned hover
 //     popup never toggles -- i upgrades it to pinned.)  No key is consumed
 //     except the one Escape that closes the popup (Escape unwinds one layer
@@ -48,7 +48,7 @@
 import { css, html, LitElement, type TemplateResult } from "lit";
 import { customElement, state } from "lit/decorators.js";
 import { placeInfopop } from "./infopop-place.js";
-import type { SwInfoDetail } from "./yk-info.js";
+import type { YkInfoDetail } from "./yk-info.js";
 
 export const HOVER_DELAY_MS = 350;
 
@@ -60,8 +60,8 @@ interface AriaReflection {
 	ariaDescribedByElements: Element[] | null;
 }
 
-@customElement("sw-infopop")
-export class SwInfopop extends LitElement {
+@customElement("yk-infopop")
+export class YkInfopop extends LitElement {
 	static styles = css`
 		:host {
 			position: fixed;
@@ -72,11 +72,11 @@ export class SwInfopop extends LitElement {
 			visibility: hidden;
 			pointer-events: none;
 			box-sizing: border-box;
-			padding: var(--sw-space-sm) var(--sw-space-md);
-			background: var(--sw-bg-raised);
-			border: 1px solid var(--sw-border);
-			color: var(--sw-text-hi);
-			font: var(--sw-type-body) var(--sw-font);
+			padding: var(--yk-space-sm) var(--yk-space-md);
+			background: var(--yk-bg-raised);
+			border: 1px solid var(--yk-border);
+			color: var(--yk-text-hi);
+			font: var(--yk-type-body) var(--yk-font);
 		}
 	`;
 
@@ -90,7 +90,7 @@ export class SwInfopop extends LitElement {
 	#timer: number | undefined;
 	#root: Node | null = null;
 	// Set when an i keydown dismisses a pinned popup: the producer answering that
-	// same keypress will usually dispatch a fresh sw-info for the same anchor
+	// same keypress will usually dispatch a fresh yk-info for the same anchor
 	// (our capture close runs first), and swallowing that arrival is what makes
 	// i a toggle.  Scoped by the keydown event itself: its eventPhase returns to
 	// NONE once its dispatch ends, so a later i escapes suppression naturally,
@@ -117,7 +117,7 @@ export class SwInfopop extends LitElement {
 		// never dispatched here) -- a row-to-aux move inside a component would be
 		// invisible.  pointermove has no relatedTarget, is never suppressed, and
 		// its composedPath still names the deep target through open shadow roots.
-		root.addEventListener("sw-info", this.#onSwInfo);
+		root.addEventListener("yk-info", this.#onYkInfo);
 		root.addEventListener("pointermove", this.#onPointermove);
 		root.addEventListener("pointerout", this.#onPointerout);
 		// Dismissals, global by nature.  Capture phase: closing must precede the
@@ -134,7 +134,7 @@ export class SwInfopop extends LitElement {
 		this.#dismissal = null;
 		const root = this.#root;
 		if (root !== null) {
-			root.removeEventListener("sw-info", this.#onSwInfo);
+			root.removeEventListener("yk-info", this.#onYkInfo);
 			root.removeEventListener("pointermove", this.#onPointermove);
 			root.removeEventListener("pointerout", this.#onPointerout);
 			this.#root = null;
@@ -144,9 +144,9 @@ export class SwInfopop extends LitElement {
 		window.removeEventListener("scroll", this.#onScroll, { capture: true });
 	}
 
-	#onSwInfo = (e: Event): void => {
-		const { content, source } = (e as CustomEvent<SwInfoDetail>).detail;
-		// The toggle's second half: this sw-info answers the very keypress that
+	#onYkInfo = (e: Event): void => {
+		const { content, source } = (e as CustomEvent<YkInfoDetail>).detail;
+		// The toggle's second half: this yk-info answers the very keypress that
 		// just dismissed a pinned popup on the same anchor (that keydown is still
 		// mid-dispatch), so it is a dismissal confirmed, not a request to reopen.
 		const d = this.#dismissal;
@@ -210,7 +210,7 @@ export class SwInfopop extends LitElement {
 		// popup must still navigate; that is how the popup follows row focus).
 		if (e.key === "Shift" || e.key === "Control" || e.key === "Alt" || e.key === "Meta") return;
 		// The toggle's first half: i closing a pinned popup notes which anchor this
-		// keypress dismissed, so #onSwInfo can tell a same-anchor re-raise (toggle
+		// keypress dismissed, so #onYkInfo can tell a same-anchor re-raise (toggle
 		// off) from a different-anchor follow (reopen there).
 		if (this.#mode === "pinned" && this.#anchor !== null && e.key.toLowerCase() === "i") {
 			this.#dismissal = { anchor: this.#anchor, keydown: e };
@@ -304,6 +304,6 @@ function nearestCarrier(path: EventTarget[]): HTMLElement | null {
 
 declare global {
 	interface HTMLElementTagNameMap {
-		"sw-infopop": SwInfopop;
+		"yk-infopop": YkInfopop;
 	}
 }
diff --git a/packages/yukey/src/yk-list.gallery.test.ts b/packages/yukey/src/yk-list.gallery.test.ts
index 014266b..93039c0 100644
--- a/packages/yukey/src/yk-list.gallery.test.ts
+++ b/packages/yukey/src/yk-list.gallery.test.ts
@@ -1,11 +1,11 @@
 // @vitest-environment jsdom
 //
-// Tests for the sw-list gallery demo AS A CONSUMER of the component: the owner's
-// commit handler (a cancelable sw-list-add append), the task-key routing that must
-// skip sw-list's structural rows, and the composed behaviors only a full console
+// Tests for the yk-list gallery demo AS A CONSUMER of the component: the owner's
+// commit handler (a cancelable yk-list-add append), the task-key routing that must
+// skip yk-list's structural rows, and the composed behaviors only a full console
 // exhibits (the churn lockout swallowing t mid-fallback).  The norm MECHANICS --
-// dive/commit/abandon, t/n resolution, group identity -- are sw-list's own and are
-// pinned in sw-list.test.ts; this file deliberately does not re-test them.
+// dive/commit/abandon, t/n resolution, group identity -- are yk-list's own and are
+// pinned in yk-list.test.ts; this file deliberately does not re-test them.
 //
 // jsdom note: real browsers fire `change` on blur-with-changed-value and both
 // text-mode exits blur the input, so the abandon test dispatches the change event
@@ -15,9 +15,9 @@
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
 import "./yk-list.gallery.js";
 import { installTokens } from "./tokens.js";
-import type { SwList } from "./yk-list.js";
+import type { YkList } from "./yk-list.js";
 
-// The task removal path flashes the x (press-flash resolves --sw-press-ms from
+// The task removal path flashes the x (press-flash resolves --yk-press-ms from
 // the live cascade and throws when it can't), so the tokens must be installed.
 installTokens();
 
@@ -25,7 +25,7 @@ interface Demo extends HTMLElement {
 	updateComplete: Promise<boolean>;
 }
 
-// jsdom has no matchMedia (sw-live-list asks about prefers-reduced-motion) and
+// jsdom has no matchMedia (yk-live-list asks about prefers-reduced-motion) and
 // implements no scrollIntoView (the controller scrolls on focus identity change).
 beforeEach(() => {
 	vi.useFakeTimers();
@@ -41,7 +41,7 @@ interface Rig {
 	demo: Demo;
 	root: ShadowRoot;
 	container: HTMLElement;
-	list: SwList;
+	list: YkList;
 	input: HTMLInputElement;
 	key: (key: string, target?: HTMLElement) => KeyboardEvent;
 	focusedRow: () => string | null;
@@ -52,22 +52,22 @@ interface Rig {
 // The drain variant (no refill attribute): starts empty, and its drain interval
 // never fires under fake timers, so every task in the list is one a test put there.
 async function makeRig(): Promise<Rig> {
-	const demo = document.createElement("swg-livecolumn-demo") as Demo;
+	const demo = document.createElement("ykg-livecolumn-demo") as Demo;
 	document.body.appendChild(demo);
 	await demo.updateComplete;
 	const root = demo.shadowRoot;
 	if (root === null) throw new Error("demo did not render a shadow root");
 	const container = root.querySelector<HTMLElement>("[data-mc-container]");
-	const list = root.querySelector<SwList>("sw-list");
-	if (container === null || list === null) throw new Error("demo missing container or sw-list");
+	const list = root.querySelector<YkList>("yk-list");
+	if (container === null || list === null) throw new Error("demo missing container or yk-list");
 	await list.updateComplete;
 	const input = list.querySelector<HTMLInputElement>("[data-mc-input]");
 	if (input === null) throw new Error("demo missing the add input");
-	// The task-removal path flashes the x (press-flash resolves --sw-press-ms from
+	// The task-removal path flashes the x (press-flash resolves --yk-press-ms from
 	// the live cascade and throws when it can't), and jsdom does not carry custom
 	// properties from the document's token stylesheet across the demo's shadow
 	// boundary -- so seed the property inline, inside the shadow tree.
-	container.style.setProperty("--sw-press-ms", "140ms");
+	container.style.setProperty("--yk-press-ms", "140ms");
 	const key = (k: string, target: HTMLElement = container): KeyboardEvent => {
 		const e = new KeyboardEvent("keydown", { key: k, bubbles: true, cancelable: true });
 		target.dispatchEvent(e);
@@ -87,19 +87,19 @@ async function makeRig(): Promise<Rig> {
 // Seed one task through the add-row (the drain queue starts empty) and land row
 // focus back on it.
 const seedTask = async (rig: Rig, label: string): Promise<void> => {
-	rig.key("a"); // sw-list's marked direct hotkey: dives into text entry
+	rig.key("a"); // yk-list's marked direct hotkey: dives into text entry
 	rig.input.value = label;
 	rig.key("Enter", rig.input);
 	await rig.settle();
 };
 
-describe("the demo as an sw-list consumer", () => {
-	it("composes the console: prepend row, sw-list (header first), status row after", async () => {
+describe("the demo as a yk-list consumer", () => {
+	it("composes the console: prepend row, yk-list (header first), status row after", async () => {
 		const { container, key, focusedRow } = await makeRig();
 		key("Home");
 		expect(focusedRow()).toBe("prepend");
 		key("ArrowDown");
-		expect(focusedRow()).toBe("queue-header"); // sw-list's structural id: name="queue"
+		expect(focusedRow()).toBe("queue-header"); // yk-list's structural id: name="queue"
 		expect(container.getAttribute("aria-activedescendant")).toBe("mc-queue-header");
 		key("End");
 		expect(focusedRow()).toBe("status");
@@ -113,7 +113,7 @@ describe("the demo as an sw-list consumer", () => {
 		rig.key("Enter", rig.input);
 		await rig.settle();
 		expect(rig.taskLabels()).toEqual(["first", "second"]);
-		const rows = [...rig.list.querySelectorAll<HTMLElement>("sw-live-list [data-mc-row]")];
+		const rows = [...rig.list.querySelectorAll<HTMLElement>("yk-live-list [data-mc-row]")];
 		expect(rows[1]?.querySelector(".origin")?.classList.contains("player")).toBe(true);
 		expect(rig.root.querySelector(".log")?.textContent).toBe("second appended");
 	});
@@ -131,7 +131,7 @@ describe("the demo as an sw-list consumer", () => {
 		expect(rig.root.querySelector(".log")?.textContent).toBe(logBefore);
 	});
 
-	it("task keys skip sw-list's structural rows (header and add-row decline quietly)", async () => {
+	it("task keys skip yk-list's structural rows (header and add-row decline quietly)", async () => {
 		const rig = await makeRig();
 		const logBefore = rig.root.querySelector(".log")?.textContent;
 		rig.key("Home");
@@ -146,10 +146,10 @@ describe("the demo as an sw-list consumer", () => {
 		expect(rig.root.querySelector(".log")?.textContent).toBe(logBefore);
 	});
 
-	it("x on a task row removes it (the owner's key semantics ride through sw-list)", async () => {
+	it("x on a task row removes it (the owner's key semantics ride through yk-list)", async () => {
 		const rig = await makeRig();
 		await seedTask(rig, "doomed");
-		rig.key("t"); // sw-list's jump to the header
+		rig.key("t"); // yk-list's jump to the header
 		rig.key("ArrowDown"); // the task
 		expect(rig.focusedRow()).toBe("t1");
 		rig.key("x");
diff --git a/packages/yukey/src/yk-list.gallery.ts b/packages/yukey/src/yk-list.gallery.ts
index 8d68147..f45cd3d 100644
--- a/packages/yukey/src/yk-list.gallery.ts
+++ b/packages/yukey/src/yk-list.gallery.ts
@@ -1,11 +1,11 @@
-// Gallery entry for the live megacolumn: sw-list (the list region: header row,
+// Gallery entry for the live megacolumn: yk-list (the list region: header row,
 // live rows, add-row, t/n) composed into a megacolumn console.  This demo is the
 // rehearsal for the infocard's task queue: a queue that churns on its own (heads
 // complete, assignments refill) AND under player command (prepend, remove, append),
 // fully keyboard-drivable, with focus surviving every kind of row death.  The demo
 // is deliberately a THIN consumer: it owns the data, the row rendering, and what
-// the keys MEAN on task rows; sw-list owns the list norms (sw-list.ts holds that
-// contract); sw-live-list under it owns row lifecycle.  Copy this shape at
+// the keys MEAN on task rows; yk-list owns the list norms (yk-list.ts holds that
+// contract); yk-live-list under it owns row lifecycle.  Copy this shape at
 // migration time.
 
 import { css, html, LitElement, nothing, type TemplateResult } from "lit";
@@ -14,7 +14,7 @@ import { classMap } from "lit/directives/class-map.js";
 import type { GalleryEntry } from "./gallery/entry.js";
 import { MegacolumnController, type MegacolumnKeyDetail, megacolumnStyles } from "./megacolumn-controller.js";
 import { flashPress } from "./press-flash.js";
-import type { SwListAddDetail } from "./yk-list.js";
+import type { YkListAddDetail } from "./yk-list.js";
 import type { LiveListPhase } from "./yk-live-list.js";
 import "./yk-list.js";
 import "./yk-button.js";
@@ -28,10 +28,10 @@ interface QueueTask {
 const taskKey = (t: QueueTask): string => t.id;
 
 // The task-queue console: one megacolumn whose lower section is a live list.
-// The list stamps row identity itself (sw-live-list.ts holds that contract);
+// The list stamps row identity itself (yk-live-list.ts holds that contract);
 // this owner renders row CONTENT (main/aux/data-info) and gives the keys meaning.
-@customElement("swg-livecolumn-demo")
-export class SwgLivecolumnDemo extends LitElement {
+@customElement("ykg-livecolumn-demo")
+export class YkgLivecolumnDemo extends LitElement {
 	static styles = [
 		megacolumnStyles,
 		css`
@@ -39,40 +39,40 @@ export class SwgLivecolumnDemo extends LitElement {
 				display: block;
 			}
 			[data-mc-container] {
-				border: 1px solid var(--sw-border-dim);
-				padding: var(--sw-space-md);
+				border: 1px solid var(--yk-border-dim);
+				padding: var(--yk-space-md);
 			}
 			.title {
-				color: var(--sw-text-dim);
-				font-size: var(--sw-type-small);
-				letter-spacing: var(--sw-tracking-wide);
+				color: var(--yk-text-dim);
+				font-size: var(--yk-type-small);
+				letter-spacing: var(--yk-tracking-wide);
 				text-transform: uppercase;
-				margin-bottom: var(--sw-space-sm);
+				margin-bottom: var(--yk-space-sm);
 			}
 			.section {
-				color: var(--sw-text-faint);
-				font-size: var(--sw-type-micro);
-				letter-spacing: var(--sw-tracking-wide);
+				color: var(--yk-text-faint);
+				font-size: var(--yk-type-micro);
+				letter-spacing: var(--yk-tracking-wide);
 				text-transform: uppercase;
-				margin: var(--sw-space-xl) 0 var(--sw-space-xs);
+				margin: var(--yk-space-xl) 0 var(--yk-space-xs);
 			}
 			.section:first-of-type {
 				margin-top: 0;
 			}
-			/* The list norms (header wash, ghosted add-row, hotkey gold) are sw-list's
+			/* The list norms (header wash, ghosted add-row, hotkey gold) are yk-list's
 			   own styles now; this sheet only spaces the list within the console. */
-			sw-list {
-				margin-top: var(--sw-space-xl);
+			yk-list {
+				margin-top: var(--yk-space-xl);
 			}
 			[data-mc-row] {
-				min-height: var(--sw-hit-min-h);
+				min-height: var(--yk-hit-min-h);
 			}
 			.action-row {
 				display: flex;
 				align-items: center;
-				padding: 2px var(--sw-space-sm);
+				padding: 2px var(--yk-space-sm);
 			}
-			.action-row sw-button {
+			.action-row yk-button {
 				flex: 1;
 			}
 			/* Task-row content (rendered by the live list into this shadow root,
@@ -80,9 +80,9 @@ export class SwgLivecolumnDemo extends LitElement {
 			.task {
 				display: flex;
 				align-items: center;
-				gap: var(--sw-space-sm);
-				min-height: var(--sw-hit-min-h);
-				padding: 2px var(--sw-space-sm);
+				gap: var(--yk-space-sm);
+				min-height: var(--yk-hit-min-h);
+				padding: 2px var(--yk-space-sm);
 				white-space: nowrap;
 			}
 			.task.committed {
@@ -91,11 +91,11 @@ export class SwgLivecolumnDemo extends LitElement {
 			.task .origin {
 				flex: none;
 				width: 88px;
-				font-size: var(--sw-type-small);
-				color: var(--sw-text-faint);
+				font-size: var(--yk-type-small);
+				color: var(--yk-text-faint);
 			}
 			.task .origin.player {
-				color: var(--sw-good);
+				color: var(--yk-good);
 			}
 			.task .grow {
 				flex: 1;
@@ -110,40 +110,40 @@ export class SwgLivecolumnDemo extends LitElement {
 			   not [data-mc-aux]: the ghost drops that attribute in the same re-render,
 			   and attribute-keyed styling would un-match mid-flash. */
 			.task .glyph {
-				color: var(--sw-danger);
+				color: var(--yk-danger);
 				border: 1px solid transparent;
-				min-width: var(--sw-hit-min-w);
-				min-height: var(--sw-hit-min-h);
+				min-width: var(--yk-hit-min-w);
+				min-height: var(--yk-hit-min-h);
 				display: inline-flex;
 				align-items: center;
 				justify-content: center;
 				box-sizing: border-box;
 			}
 			.task .glyph.gone {
-				color: var(--sw-good);
+				color: var(--yk-good);
 			}
 			/* The aux's own pressed look: danger-family, not the blue activation wash --
 			   ownership distinguishes the flashes, no enumeration of activation kinds. */
-			.task .glyph.sw-pressed {
-				background: hsl(from var(--sw-danger) h calc(s * 2) calc(l * 0.75) / 0.6);
-				color: var(--sw-text-bright);
+			.task .glyph.yk-pressed {
+				background: hsl(from var(--yk-danger) h calc(s * 2) calc(l * 0.75) / 0.6);
+				color: var(--yk-text-bright);
 			}
 			.idle {
-				color: var(--sw-text-faint);
-				padding: 2px var(--sw-space-sm);
-				line-height: var(--sw-hit-min-h);
+				color: var(--yk-text-faint);
+				padding: 2px var(--yk-space-sm);
+				line-height: var(--yk-hit-min-h);
 			}
 			.status-row {
 				display: flex;
 				align-items: center;
-				color: var(--sw-text-dim);
-				padding: 2px var(--sw-space-sm);
-				margin-top: var(--sw-space-xl);
+				color: var(--yk-text-dim);
+				padding: 2px var(--yk-space-sm);
+				margin-top: var(--yk-space-xl);
 			}
 			.log {
-				margin-top: var(--sw-space-md);
-				color: var(--sw-good);
-				font-size: var(--sw-type-ui);
+				margin-top: var(--yk-space-md);
+				color: var(--yk-good);
+				font-size: var(--yk-type-ui);
 				min-height: 1.4em;
 			}
 		`,
@@ -208,24 +208,24 @@ export class SwgLivecolumnDemo extends LitElement {
 		this.lastAction = `${task.label} prepended`;
 	}
 
-	// The add-row's commit, arriving as sw-list's cancelable sw-list-add event
-	// (sw-list owns the dive/abandon/ghost mechanics and the nothing-syncs-until-
+	// The add-row's commit, arriving as yk-list's cancelable yk-list-add event
+	// (yk-list owns the dive/abandon/ghost mechanics and the nothing-syncs-until-
 	// commit contract; the owner just appends).  Not cancelled here: any trimmed
 	// non-empty text is a legal task.
 	private onListAdd(e: Event): void {
-		const { text } = (e as CustomEvent<SwListAddDetail>).detail;
+		const { text } = (e as CustomEvent<YkListAddDetail>).detail;
 		this.seq += 1;
 		this.tasks = [...this.tasks, { id: `t${this.seq}`, label: text, origin: "player" }];
 		this.lastAction = `${text} appended`;
 	}
 
-	// The queue rows' key semantics (the prepend row's keys are sw-button's own
+	// The queue rows' key semantics (the prepend row's keys are yk-button's own
 	// business -- direct-marked, it claims them before this handler could matter):
 	// x/Delete from anywhere in the row removes; Enter/Space on the aux removes;
 	// Enter/Space on main inspects (activation must not be destructive).
 	private onMegacolumnKey(e: Event): void {
 		const { detail } = e as CustomEvent<MegacolumnKeyDetail>;
-		// Only task rows have task semantics: sw-list's structural rows (the
+		// Only task rows have task semantics: yk-list's structural rows (the
 		// "<name>-header"/"<name>-add" id scheme is its contract) and the demo's
 		// own prepend/status rows decline everything here (the add-row's typing
 		// never reaches this handler; text entry dominates).
@@ -307,20 +307,20 @@ export class SwgLivecolumnDemo extends LitElement {
 					<!-- Direct-marked (the deliberate exception to hotkeys-move-focus):
 					     a prepend is cheaply undone -- x removes the new head -- so the
 					     see-before-commit step would only slow the order down. -->
-					<sw-button
+					<yk-button
 						label="prepend priority task"
 						hotkey="p"
 						hotkey-direct
 						info="queues a player task at the HEAD of the queue; watch it appear instantly even mid-keyboard-burst"
 						@click=${() => this.prependTask()}
-					></sw-button>
+					></yk-button>
 				</div>
 
-				<!-- The list region: sw-list owns the norms (header row speaking for the
+				<!-- The list region: yk-list owns the norms (header row speaking for the
 				     region, ghosted add-row with the marked direct a, t/n jumps); this
 				     owner supplies data, row rendering, and the commit handler.  The
-				     norm teaching in the info texts is sw-list's own appendix. -->
-				<sw-list
+				     norm teaching in the info texts is yk-list's own appendix. -->
+				<yk-list
 					name="queue"
 					header="task queue (live)"
 					header-info="the task queue; every row is removable -- x or Delete"
@@ -330,8 +330,8 @@ export class SwgLivecolumnDemo extends LitElement {
 					.keyOf=${taskKey}
 					.renderItem=${this.renderTask}
 					.renderEmpty=${this.renderQueueEmpty}
-					@sw-list-add=${this.onListAdd}
-				></sw-list>
+					@yk-list-add=${this.onListAdd}
+				></yk-list>
 
 				<!-- A stop AFTER the list, so n has somewhere to land (and the demo
 				     shows what n is for: hopping past a long list without arrowing through
@@ -350,10 +350,10 @@ export class SwgLivecolumnDemo extends LitElement {
 }
 
 const entry: GalleryEntry = {
-	id: "sw-list",
-	title: "sw-list: the live megacolumn's list region",
+	id: "yk-list",
+	title: "yk-list: the live megacolumn's list region",
 	description: "The list region component, composed here into a megacolumn console: header row, live rows "
-		+ "(sw-live-list inside), ghosted add-row, and the t/n list-scoped jumps -- the list norms as "
+		+ "(yk-live-list inside), ghosted add-row, and the t/n list-scoped jumps -- the list norms as "
 		+ "things the component does.  The queue churns and stays fully keyboard-drivable: ghost rows "
 		+ "stay navigable while they hold (a stationary ghost is safe for the keyboard exactly as it "
 		+ "is for the mouse); collapsing rows leave navigation and focus falls to a neighbor under a "
@@ -370,7 +370,7 @@ const entry: GalleryEntry = {
 				+ "Removals flash the × itself, danger-tinted, before the row ghosts (x/Delete removes a task from anywhere in its row; "
 				+ "right-arrow reaches the ×; Enter on a ghost still inspects it), and if focus was on a ghost "
 				+ "when it collapsed, focus falls to the next row under a brief decisive-key lockout -- hammer "
-				+ "Enter across the collapse and note nothing mis-fires.  The LIST NORMS are sw-list's own "
+				+ "Enter across the collapse and note nothing mis-fires.  The LIST NORMS are yk-list's own "
 				+ "behavior now: a header row (arrow onto it and the "
 				+ "whole region washes -- group identity without a nested container; i there describes the list as "
 				+ "a list) and the add-row at the tail.  Two ways to add, both earning the direct marking under "
@@ -379,19 +379,19 @@ const entry: GalleryEntry = {
 				+ "(focus following to the orders row), justified because a prepend is cheaply undone -- x the new "
 				+ "head and it is gone.  The add-row's a dives straight into typing (direct on a text row means "
 				+ "what Enter means there), justified because nothing touches the queue until the Enter commit "
-				+ "(the owner hears a cancelable sw-list-add and appends; watch the task animate in) and Escape "
+				+ "(the owner hears a cancelable yk-list-add and appends; watch the task animate in) and Escape "
 				+ "abandons the draft.  While focus is anywhere "
 				+ "in the list -- header, tasks, add-row -- t jumps back up to the header and n jumps past the "
-				+ "list to the next stop after it: list-scoped keys sw-list wires itself, deliberately not "
+				+ "list to the next stop after it: list-scoped keys yk-list wires itself, deliberately not "
 				+ "gold-marked (they mean the same thing in every list).",
-			render: () => html`<swg-livecolumn-demo refill></swg-livecolumn-demo>`,
+			render: () => html`<ykg-livecolumn-demo refill></ykg-livecolumn-demo>`,
 		},
 		{
 			caption: "Drain to empty: nothing refills this queue.  Prepend a few tasks (p), watch them complete and "
 				+ "drain away; the empty state is the owner's renderEmpty render-prop (mega mode has no slots).  "
 				+ "Focus stays sane through the last row's death: the column itself keeps focus and the arrows "
 				+ "revive it the moment a row exists again.",
-			render: () => html`<swg-livecolumn-demo label="drain queue"></swg-livecolumn-demo>`,
+			render: () => html`<ykg-livecolumn-demo label="drain queue"></ykg-livecolumn-demo>`,
 		},
 	],
 };
diff --git a/packages/yukey/src/yk-list.test.ts b/packages/yukey/src/yk-list.test.ts
index 7a9460c..30ccf92 100644
--- a/packages/yukey/src/yk-list.test.ts
+++ b/packages/yukey/src/yk-list.test.ts
@@ -1,9 +1,9 @@
 // @vitest-environment jsdom
 //
-// Tests for sw-list: the list region component (header row, live rows, add-row,
+// Tests for yk-list: the list region component (header row, live rows, add-row,
 // t/n list-scoped keys).  The rig is a minimal owner: a Lit host with a
 // MegacolumnController, one row before the list, one after (n's landing), and
-// an sw-list whose knobs the tests flip per case.  The add-row's commit/abandon
+// a yk-list whose knobs the tests flip per case.  The add-row's commit/abandon
 // distinction is the subtle part: real browsers fire `change` on
 // blur-with-changed-value and BOTH text-mode exits blur the input, so a commit
 // hooked to @change would fire on Escape too -- jsdom fires no change on its
@@ -14,7 +14,7 @@ import { html, LitElement, nothing, type TemplateResult } from "lit";
 import { customElement, property } from "lit/decorators.js";
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
 import { MegacolumnController, megacolumnStyles } from "./megacolumn-controller.js";
-import type { SwList, SwListAddDetail } from "./yk-list.js";
+import type { YkList, YkListAddDetail } from "./yk-list.js";
 import type { LiveListPhase } from "./yk-live-list.js";
 import "./yk-list.js";
 
@@ -23,7 +23,7 @@ interface Item {
 	label: string;
 }
 
-// jsdom has no matchMedia (sw-live-list asks about prefers-reduced-motion) and
+// jsdom has no matchMedia (yk-live-list asks about prefers-reduced-motion) and
 // implements no scrollIntoView (the controller scrolls on focus identity change).
 beforeEach(() => {
 	vi.useFakeTimers();
@@ -35,10 +35,10 @@ afterEach(() => {
 });
 Element.prototype.scrollIntoView = (): void => {};
 
-// The minimal owner.  `withAfter` controls whether n has a landing; the sw-list
+// The minimal owner.  `withAfter` controls whether n has a landing; the yk-list
 // knobs pass straight through.
-@customElement("swt-list-owner")
-class SwtListOwner extends LitElement {
+@customElement("ykt-list-owner")
+class YktListOwner extends LitElement {
 	static styles = [megacolumnStyles];
 	// biome-ignore lint/correctness/noUnusedPrivateClassMembers: wires itself via addController
 	private mc = new MegacolumnController(this);
@@ -56,7 +56,7 @@ class SwtListOwner extends LitElement {
 	render(): TemplateResult {
 		return html`<div data-mc-container aria-label="test column">
 			<div data-mc-row="before" data-info="the row before the list">before</div>
-			<sw-list
+			<yk-list
 				name="tl"
 				header=${this.header}
 				header-info="a list of test items"
@@ -66,39 +66,39 @@ class SwtListOwner extends LitElement {
 				.items=${this.items}
 				.keyOf=${(it: Item) => it.id}
 				.renderItem=${(it: Item, _phase: LiveListPhase) => html`<div class="row-content">${it.label}</div>`}
-			></sw-list>
+			></yk-list>
 			${this.withAfter ? html`<div data-mc-row="after">after</div>` : nothing}
 		</div>`;
 	}
 }
 
 interface Rig {
-	owner: SwtListOwner;
+	owner: YktListOwner;
 	root: ShadowRoot;
 	container: HTMLElement;
-	list: SwList<Item>;
+	list: YkList<Item>;
 	input: HTMLInputElement | null;
 	key: (key: string, target?: HTMLElement) => KeyboardEvent;
 	focusedRow: () => string | null;
-	adds: SwListAddDetail[];
+	adds: YkListAddDetail[];
 	settle: () => Promise<void>;
 }
 
-async function makeRig(setup?: (owner: SwtListOwner) => void): Promise<Rig> {
-	const owner = document.createElement("swt-list-owner") as SwtListOwner;
+async function makeRig(setup?: (owner: YktListOwner) => void): Promise<Rig> {
+	const owner = document.createElement("ykt-list-owner") as YktListOwner;
 	setup?.(owner);
 	document.body.appendChild(owner);
 	await owner.updateComplete;
 	const root = owner.shadowRoot;
 	if (root === null) throw new Error("owner did not render a shadow root");
 	const container = root.querySelector<HTMLElement>("[data-mc-container]");
-	const list = root.querySelector<SwList<Item>>("sw-list");
-	if (container === null || list === null) throw new Error("rig missing container or sw-list");
+	const list = root.querySelector<YkList<Item>>("yk-list");
+	if (container === null || list === null) throw new Error("rig missing container or yk-list");
 	await list.updateComplete;
 	const input = list.querySelector<HTMLInputElement>("[data-mc-input]");
-	const adds: SwListAddDetail[] = [];
-	list.addEventListener("sw-list-add", (e) => {
-		adds.push((e as CustomEvent<SwListAddDetail>).detail);
+	const adds: YkListAddDetail[] = [];
+	list.addEventListener("yk-list-add", (e) => {
+		adds.push((e as CustomEvent<YkListAddDetail>).detail);
 	});
 	const key = (k: string, target: HTMLElement = container): KeyboardEvent => {
 		const e = new KeyboardEvent("keydown", { key: k, bubbles: true, cancelable: true });
@@ -121,16 +121,16 @@ describe("structure", () => {
 		const { list } = await makeRig();
 		const rows = [...list.querySelectorAll<HTMLElement>("[data-mc-row]")];
 		expect(rows.map((r) => r.dataset.mcRow)).toEqual(["tl-header", "tl-add"]);
-		expect(list.querySelector("sw-live-list")).not.toBeNull();
-		expect(list.querySelector(".sw-list-header")?.textContent).toBe("the test list");
+		expect(list.querySelector("yk-live-list")).not.toBeNull();
+		expect(list.querySelector(".yk-list-header")?.textContent).toBe("the test list");
 	});
 
-	it("passes items through to the inner sw-live-list (rows render, keyed)", async () => {
+	it("passes items through to the inner yk-live-list (rows render, keyed)", async () => {
 		const { owner, list, settle } = await makeRig((o) => {
 			o.items = items("a", "b");
 		});
 		await settle();
-		const live = list.querySelector("sw-live-list");
+		const live = list.querySelector("yk-live-list");
 		await (live as LitElement | null)?.updateComplete;
 		const labels = [...list.querySelectorAll(".row-content")].map((el) => el.textContent);
 		expect(labels).toEqual(["item a", "item b"]);
@@ -146,7 +146,7 @@ describe("structure", () => {
 			o.addable = false;
 		});
 		expect(list.querySelectorAll("[data-mc-row]").length).toBe(0);
-		expect(list.querySelector("sw-live-list")).not.toBeNull();
+		expect(list.querySelector("yk-live-list")).not.toBeNull();
 	});
 
 	it("the header row is a nav stop the controller sees (rescan announced)", async () => {
@@ -159,9 +159,9 @@ describe("structure", () => {
 	});
 
 	it("announces its own rescan: a structural row appearing without a live-list update", async () => {
-		// Toggling addable re-renders sw-list but not the inner sw-live-list (the
+		// Toggling addable re-renders yk-list but not the inner yk-live-list (the
 		// items reference is unchanged, so its properties report no change) -- the
-		// add-row's appearance is on sw-list's OWN announcement, nobody else's.
+		// add-row's appearance is on yk-list's OWN announcement, nobody else's.
 		const { list, key, focusedRow, settle } = await makeRig((o) => {
 			o.addable = false;
 		});
@@ -176,19 +176,19 @@ describe("structure", () => {
 
 	it("composes owner info with the norm teaching on both structural rows", async () => {
 		const { list } = await makeRig();
-		const headerInfo = list.querySelector<HTMLElement>(".sw-list-header")?.dataset.info ?? "";
+		const headerInfo = list.querySelector<HTMLElement>(".yk-list-header")?.dataset.info ?? "";
 		expect(headerInfo).toContain("a list of test items");
 		expect(headerInfo).toContain("t returns here");
 		expect(headerInfo).toContain("n jumps past the list");
 		expect(headerInfo).toContain("a dives into the add-row");
-		const addInfo = list.querySelector<HTMLElement>(".sw-list-add")?.dataset.info ?? "";
+		const addInfo = list.querySelector<HTMLElement>(".yk-list-add")?.dataset.info ?? "";
 		expect(addInfo).toContain("appends a test item");
 		expect(addInfo).toContain("Escape abandons");
 	});
 
 	it("marks the add hotkey letter in the label", async () => {
 		const { list } = await makeRig();
-		const hk = list.querySelector(".sw-list-add .sw-list-hk");
+		const hk = list.querySelector(".yk-list-add .yk-list-hk");
 		expect(hk?.textContent).toBe("a");
 		const marked = list.querySelector<HTMLElement>("[data-mc-hotkey]");
 		expect(marked?.dataset.mcHotkey).toBe("a");
@@ -201,7 +201,7 @@ describe("owner-rendered header content (renderHeader)", () => {
 		const { list, container, key, focusedRow } = await makeRig((o) => {
 			o.renderHeader = () => html`<div class="fancy">THE LIST · 3 / 9</div>`;
 		});
-		const headerRow = list.querySelector<HTMLElement>(".sw-list-header");
+		const headerRow = list.querySelector<HTMLElement>(".yk-list-header");
 		expect(headerRow?.querySelector(".fancy")?.textContent).toBe("THE LIST · 3 / 9");
 		expect(headerRow?.dataset.mcRow).toBe("tl-header");
 		key("Home");
@@ -223,7 +223,7 @@ describe("owner-rendered header content (renderHeader)", () => {
 			o.header = "";
 			o.renderHeader = () => html`<div class="fancy">orphan content</div>`;
 		});
-		expect(list.querySelector(".sw-list-header")).toBeNull();
+		expect(list.querySelector(".yk-list-header")).toBeNull();
 		expect(list.querySelector(".fancy")).toBeNull();
 	});
 });
@@ -233,7 +233,7 @@ describe("group identity (the header-role decision)", () => {
 		const { list } = await makeRig();
 		expect(list.getAttribute("role")).toBe("group");
 		expect(list.getAttribute("aria-label")).toBe("the test list");
-		expect(list.querySelector(".sw-list-header")?.getAttribute("role")).toBe("option");
+		expect(list.querySelector(".yk-list-header")?.getAttribute("role")).toBe("option");
 	});
 
 	it("headerless: no group role, no label", async () => {
@@ -245,11 +245,11 @@ describe("group identity (the header-role decision)", () => {
 	});
 
 	it("an author-set role is left alone", async () => {
-		const owner = document.createElement("swt-list-owner") as SwtListOwner;
+		const owner = document.createElement("ykt-list-owner") as YktListOwner;
 		document.body.appendChild(owner);
 		await owner.updateComplete;
-		const list = owner.shadowRoot?.querySelector<SwList<Item>>("sw-list");
-		if (list === undefined || list === null) throw new Error("no sw-list");
+		const list = owner.shadowRoot?.querySelector<YkList<Item>>("yk-list");
+		if (list === undefined || list === null) throw new Error("no yk-list");
 		// Re-run the role logic with an author role in place: simulate by clearing
 		// ours, setting an author's, and forcing an update.
 		list.removeAttribute("role");
@@ -314,7 +314,7 @@ describe("the add-row", () => {
 	it("a refused commit (preventDefault) keeps the draft and text mode", async () => {
 		const { root, container, list, input, key } = await makeRig();
 		if (input === null) throw new Error("no input");
-		list.addEventListener("sw-list-add", (e) => e.preventDefault(), { once: true });
+		list.addEventListener("yk-list-add", (e) => e.preventDefault(), { once: true });
 		key("a");
 		input.value = "rejected by validation";
 		key("Enter", input);
@@ -460,7 +460,7 @@ describe("fail closed", () => {
 
 	it("throws outside any [data-mc-container]", async () => {
 		// The connect throw surfaces immediately; the already-scheduled first render
-		// then raises the same complaint from the inner sw-live-list (jsdom reports
+		// then raises the same complaint from the inner yk-live-list (jsdom reports
 		// custom-element reaction throws as window errors, asynchronously) -- so the
 		// capture stays open across the settled update.
 		let list: LitElement | undefined;
@@ -471,7 +471,7 @@ describe("fail closed", () => {
 		};
 		window.addEventListener("error", onError);
 		try {
-			list = document.createElement("sw-list") as LitElement;
+			list = document.createElement("yk-list") as LitElement;
 			document.body.appendChild(list);
 			await list.updateComplete.catch(() => {});
 		} finally {
@@ -485,7 +485,7 @@ describe("fail closed", () => {
 		container.setAttribute("data-mc-container", "");
 		document.body.appendChild(container);
 		const reported = reportsFrom(() => {
-			const list = document.createElement("sw-list");
+			const list = document.createElement("yk-list");
 			list.innerHTML = "<div>nope</div>";
 			container.appendChild(list);
 		});
@@ -517,7 +517,7 @@ describe("fail closed", () => {
 		};
 		window.addEventListener("error", onError);
 		try {
-			const twin = document.createElement("sw-list") as SwList<Item>;
+			const twin = document.createElement("yk-list") as YkList<Item>;
 			twin.name = "tl";
 			twin.header = "the twin";
 			twin.keyOf = (it: Item) => it.id;
diff --git a/packages/yukey/src/yk-list.ts b/packages/yukey/src/yk-list.ts
index 57e437e..d11e310 100644
--- a/packages/yukey/src/yk-list.ts
+++ b/packages/yukey/src/yk-list.ts
@@ -1,4 +1,4 @@
-// sw-list: the list region of a megacolumn, with the list norms built in.
+// yk-list: the list region of a megacolumn, with the list norms built in.
 // An owner composes it into its column ([data-mc-container]) and gets, by
 // construction, the behaviors the guidelines ask every list to have:
 //
@@ -7,7 +7,7 @@
 //     ring stays on the header row itself -- deliberately no perimeter border,
 //     which would read as a nested focus container.  Its data-info describes
 //     the list AS a list.  Headerless lists are legal (no header row renders).
-//   - The LIVE ROWS: an inner sw-live-list in megacolumn mode (that file owns
+//   - The LIVE ROWS: an inner yk-live-list in megacolumn mode (that file owns
 //     the row-lifecycle contract: churn safety, ghosts, geometry, the key pin).
 //   - The ADD-ROW (optional): the append affordance as a permanent ghosted nav
 //     stop at the tail.  Its marked hotkey (default a) is DIRECT: it dives
@@ -46,7 +46,7 @@
 //     addable       Renders the add-row.  Lists that cannot append (a cargo
 //                   manifest) just leave it off.
 //     add-label     The add-row's label; default "add…".  The hotkey letter is
-//                   marked in the label (first occurrence, sw-button's rule),
+//                   marked in the label (first occurrence, yk-button's rule),
 //                   or shown after it when the label lacks the letter.
 //     add-hotkey    The marked direct hotkey; default "a" (single [a-z0-9]).
 //                   Override only when two addable lists share a column: the
@@ -54,12 +54,12 @@
 //     add-info      The owner's info text for the add-row; norm teaching is
 //                   appended, as with header-info.
 //     items, keyOf, renderItem, renderEmpty, resetKey
-//                   Passed through to the inner sw-live-list untouched: the
+//                   Passed through to the inner yk-live-list untouched: the
 //                   owner renders row CONTENT and keeps data identity; see
-//                   sw-live-list.ts for their contracts.
+//                   yk-live-list.ts for their contracts.
 //
 //   events:
-//     sw-list-add   The add-row's commit (bubbling, cancelable; detail
+//     yk-list-add   The add-row's commit (bubbling, cancelable; detail
 //                   { text: string }, trimmed, never empty).  Dispatched only on
 //                   the input's own Enter keydown -- never on change/blur, so an
 //                   Escape that blurs the input cannot masquerade as a commit.
@@ -71,17 +71,17 @@
 //
 //   key semantics stay the owner's: megacolumn-key listeners (what Enter, x,
 //   Delete MEAN on a live row) belong on the owner's container, exactly as
-//   without sw-list.  The structural rows decline every key -- owners routing
+//   without yk-list.  The structural rows decline every key -- owners routing
 //   by rowId should skip "<name>-header" and "<name>-add" (that id scheme is
 //   public contract).  Owners claiming letters that overlap t/n or the add
 //   hotkey should respect a prior claim (check defaultPrevented), though
 //   wanting to is already a design smell.
 //
 //   rendered structure (public contract, since it renders into the owner's
-//   tree): [header row?] <sw-live-list megacolumn> [add-row?], all inside this
-//   element.  sw-live-list's feel knobs (holdMs, keyPinMs, pointerSafety...)
+//   tree): [header row?] <yk-live-list megacolumn> [add-row?], all inside this
+//   element.  yk-live-list's feel knobs (holdMs, keyPinMs, pointerSafety...)
 //   are deliberately NOT mirrored here -- mirrored twins invite drift -- and are
-//   set on the inner element where they live.  The sw-list-prefixed classes on
+//   set on the inner element where they live.  The yk-list-prefixed classes on
 //   the structural rows are stable; their look is the norm's look.
 //
 // Group identity for AT (the header-role decision, settled for flat lists):
@@ -94,12 +94,12 @@
 // An author-set role on the host is left alone.  Trees are explicitly NOT this
 // component's business: a treeitem reframe (levels, expansion state) belongs to
 // a distinct tree vocabulary, and nothing here forecloses one -- nesting
-// sw-list was never legal (rows come from renderItem, not markup).
+// yk-list was never legal (rows come from renderItem, not markup).
 //
-// Like sw-live-list's mega mode, this renders into the owner's tree (light
+// Like yk-live-list's mega mode, this renders into the owner's tree (light
 // DOM): the megacolumn scan and aria-activedescendant cannot cross shadow
 // boundaries.  No shadow root, no slots; styles adopt into the owner's root
-// once, sw-list-prefixed.  After every update it dispatches megacolumn-rescan
+// once, yk-list-prefixed.  After every update it dispatches megacolumn-rescan
 // (structural rows change under the controller's scan on this element's own
 // schedule, not the owner's).  Fail closed: connecting outside a
 // [data-mc-container] throws, element children throw, a malformed name or
@@ -112,7 +112,7 @@ import type { MegacolumnKeyDetail } from "./megacolumn-controller.js";
 import type { LiveListPhase } from "./yk-live-list.js";
 import "./yk-live-list.js";
 
-export interface SwListAddDetail {
+export interface YkListAddDetail {
 	text: string; // trimmed, never empty
 }
 
@@ -124,54 +124,54 @@ export interface SwListAddDetail {
 // raises the well and the accent border.  The raised look is keyed to the
 // add-row's OWN focus class, not just the container's text mode: two lists in
 // one column must not both light up when one of them is being typed in.
-const swListStyles = css`
-	sw-list {
+const ykListStyles = css`
+	yk-list {
 		display: block;
 	}
-	.mc-focus-within sw-list:has(> .sw-list-header.mc-row-focused) {
-		background: var(--sw-wash-faint);
+	.mc-focus-within yk-list:has(> .yk-list-header.mc-row-focused) {
+		background: var(--yk-wash-faint);
 	}
-	.sw-list-header {
+	.yk-list-header {
 		display: flex;
 		align-items: center;
-		min-height: var(--sw-hit-min-h);
-		color: var(--sw-text-faint);
-		font-size: var(--sw-type-micro);
-		letter-spacing: var(--sw-tracking-wide);
+		min-height: var(--yk-hit-min-h);
+		color: var(--yk-text-faint);
+		font-size: var(--yk-type-micro);
+		letter-spacing: var(--yk-tracking-wide);
 		text-transform: uppercase;
-		padding: 2px var(--sw-space-sm);
+		padding: 2px var(--yk-space-sm);
 		box-sizing: border-box;
 	}
-	.sw-list-add {
+	.yk-list-add {
 		display: flex;
 		align-items: center;
-		gap: var(--sw-space-sm);
-		min-height: var(--sw-hit-min-h);
-		padding: 2px var(--sw-space-sm);
-		color: var(--sw-text-faint);
+		gap: var(--yk-space-sm);
+		min-height: var(--yk-hit-min-h);
+		padding: 2px var(--yk-space-sm);
+		color: var(--yk-text-faint);
 		box-sizing: border-box;
 	}
-	.sw-list-add input[data-mc-input] {
+	.yk-list-add input[data-mc-input] {
 		flex: 1;
 		font: inherit;
-		color: var(--sw-text-bright);
+		color: var(--yk-text-bright);
 		background: transparent;
 		border: 1px solid transparent;
-		min-height: var(--sw-hit-min-h);
+		min-height: var(--yk-hit-min-h);
 		box-sizing: border-box;
-		padding: 0 var(--sw-space-sm);
+		padding: 0 var(--yk-space-sm);
 	}
-	.mc-text-mode .sw-list-add.mc-row-focused input[data-mc-input] {
-		background: var(--sw-bg-well);
-		border-color: var(--sw-accent);
+	.mc-text-mode .yk-list-add.mc-row-focused input[data-mc-input] {
+		background: var(--yk-bg-well);
+		border-color: var(--yk-accent);
 	}
-	.sw-list-hk {
-		color: var(--sw-hotkey-accent);
+	.yk-list-hk {
+		color: var(--yk-hotkey-accent);
 	}
 `;
 
-@customElement("sw-list")
-export class SwList<T = unknown> extends LitElement {
+@customElement("yk-list")
+export class YkList<T = unknown> extends LitElement {
 	@property()
 	name = "list";
 	@property()
@@ -216,17 +216,17 @@ export class SwList<T = unknown> extends LitElement {
 	protected override createRenderRoot(): HTMLElement {
 		if (this.children.length > 0) {
 			throw new Error(
-				"sw-list: renders into the light DOM and has no slots; rows come from renderItem",
+				"yk-list: renders into the light DOM and has no slots; rows come from renderItem",
 			);
 		}
-		adoptIntoRoot(this.getRootNode(), swListStyles, "data-sw-list");
+		adoptIntoRoot(this.getRootNode(), ykListStyles, "data-yk-list");
 		return this;
 	}
 
 	override connectedCallback(): void {
 		super.connectedCallback();
 		const container = this.closest<HTMLElement>("[data-mc-container]");
-		if (container === null) throw new Error("sw-list: outside any [data-mc-container]");
+		if (container === null) throw new Error("yk-list: outside any [data-mc-container]");
 		this.#container = container;
 	}
 
@@ -238,13 +238,13 @@ export class SwList<T = unknown> extends LitElement {
 	override willUpdate(): void {
 		if (!/^[a-z0-9-]+$/i.test(this.name)) {
 			throw new Error(
-				`sw-list: name "${this.name}" must be a non-empty [a-z0-9-]+ token `
+				`yk-list: name "${this.name}" must be a non-empty [a-z0-9-]+ token `
 					+ "(it namespaces the structural row ids)",
 			);
 		}
 		if (this.addable && !/^[a-z0-9]$/i.test(this.addHotkey)) {
 			throw new Error(
-				`sw-list: add-hotkey "${this.addHotkey}" must be a single [a-z0-9] character`,
+				`yk-list: add-hotkey "${this.addHotkey}" must be a single [a-z0-9] character`,
 			);
 		}
 	}
@@ -252,7 +252,7 @@ export class SwList<T = unknown> extends LitElement {
 	protected override updated(): void {
 		// Structural rows appear and disappear on this element's own update
 		// schedule, after the owner's hostUpdated scan already ran -- announce,
-		// same contract as sw-live-list (re-scans are idempotent).
+		// same contract as yk-live-list (re-scans are idempotent).
 		this.dispatchEvent(new CustomEvent("megacolumn-rescan", { bubbles: true }));
 		// Group identity for AT (the header-role decision; see the file header).
 		// An author-set role is left alone; only our own role="group" is retracted
@@ -323,7 +323,7 @@ export class SwList<T = unknown> extends LitElement {
 			input.value = "";
 			return; // an empty commit is no commit; the exit proceeds
 		}
-		const commit = new CustomEvent<SwListAddDetail>("sw-list-add", {
+		const commit = new CustomEvent<YkListAddDetail>("yk-list-add", {
 			detail: { text },
 			bubbles: true,
 			cancelable: true,
@@ -347,23 +347,23 @@ export class SwList<T = unknown> extends LitElement {
 				? nothing
 				: html`<div
 							data-mc-row="${this.name}-header"
-							class="sw-list-header"
+							class="yk-list-header"
 							data-info=${this.#composedHeaderInfo()}
 						>${this.renderHeader?.() ?? this.header}</div>`
 		}
-			<sw-live-list
+			<yk-live-list
 				megacolumn
 				.items=${this.items}
 				.keyOf=${this.keyOf}
 				.renderItem=${this.renderItem}
 				.renderEmpty=${this.renderEmpty}
 				.resetKey=${this.resetKey}
-			></sw-live-list>
+			></yk-live-list>
 			${
 			this.addable
 				? html`<div
 							data-mc-row="${this.name}-add"
-							class="sw-list-add"
+							class="yk-list-add"
 							data-info=${this.#composedAddInfo()}
 						>
 							<span data-mc-hotkey=${this.addHotkey} data-mc-hotkey-direct>+ ${this.#markedAddLabel()}</span>
@@ -391,12 +391,12 @@ export class SwList<T = unknown> extends LitElement {
 	}
 
 	// The add-row's hotkey marking: the letter accented in the label (first
-	// case-insensitive occurrence, sw-button's rule), or shown after the label
+	// case-insensitive occurrence, yk-button's rule), or shown after the label
 	// when it lacks the letter -- a marking must be visible either way.
 	#markedAddLabel(): TemplateResult {
 		const idx = this.addLabel.toLowerCase().indexOf(this.addHotkey.toLowerCase());
-		if (idx === -1) return html`${this.addLabel} <span class="sw-list-hk">${this.addHotkey}</span>`;
-		return html`${this.addLabel.slice(0, idx)}<span class="sw-list-hk">${this.addLabel[idx]}</span>${
+		if (idx === -1) return html`${this.addLabel} <span class="yk-list-hk">${this.addHotkey}</span>`;
+		return html`${this.addLabel.slice(0, idx)}<span class="yk-list-hk">${this.addLabel[idx]}</span>${
 			this.addLabel.slice(idx + 1)
 		}`;
 	}
@@ -404,6 +404,6 @@ export class SwList<T = unknown> extends LitElement {
 
 declare global {
 	interface HTMLElementTagNameMap {
-		"sw-list": SwList;
+		"yk-list": YkList;
 	}
 }
diff --git a/packages/yukey/src/yk-live-list.gallery.ts b/packages/yukey/src/yk-live-list.gallery.ts
index b796b3d..e4ec0c5 100644
--- a/packages/yukey/src/yk-live-list.gallery.ts
+++ b/packages/yukey/src/yk-live-list.gallery.ts
@@ -1,4 +1,4 @@
-// Gallery entry for sw-live-list, the keyed live list whose rows vanish safely
+// Gallery entry for yk-live-list, the keyed live list whose rows vanish safely
 // under a pointer.  The churn demo element below feeds it a queue that drains and
 // refills on a timer, which is the component's home condition: data that changes
 // out from under the user, mid-hover.  The first two variants contrast the two
@@ -29,10 +29,10 @@ const rowStyles = css`
 	.row .mark {
 		flex: none;
 		width: 16px;
-		color: var(--sw-text-faint);
+		color: var(--yk-text-faint);
 	}
 	.row .mark.live {
-		color: var(--sw-good);
+		color: var(--yk-good);
 	}
 `;
 
@@ -47,8 +47,8 @@ const keyOf = (item: DemoRow): string => item.id;
 // A queue that drains itself and gets new work, like a ship's task list does.
 // Every beat the oldest row completes (commit -> hold -> collapse) and a fresh
 // one enters at the tail; the interplay with a hovering pointer is the demo.
-@customElement("swg-live-list-churn")
-export class SwgLiveListChurn extends LitElement {
+@customElement("ykg-live-list-churn")
+export class YkgLiveListChurn extends LitElement {
 	@property()
 	pointerSafety: "click-guard" | "freeze" = "click-guard";
 	@state()
@@ -78,19 +78,19 @@ export class SwgLiveListChurn extends LitElement {
 	}
 
 	render(): TemplateResult {
-		return html`<sw-live-list
+		return html`<yk-live-list
 			.items=${this.items}
 			.keyOf=${keyOf}
 			.renderItem=${renderRow}
 			.itemStyles=${rowStyles}
 			.pointerSafety=${this.pointerSafety}
-		></sw-live-list>`;
+		></yk-live-list>`;
 	}
 }
 
 const entry: GalleryEntry = {
-	id: "sw-live-list",
-	title: "sw-live-list",
+	id: "yk-live-list",
+	title: "yk-live-list",
 	description: "A keyed live list whose rows vanish safely under a pointer.  "
 		+ "The owner supplies keying and rendering; this component owns row lifecycle: "
 		+ "departed rows ghost and hold before collapsing, insertions grow in.  "
@@ -105,23 +105,23 @@ const entry: GalleryEntry = {
 				+ "with the pointer resting inside, nothing waits for it.  Try clicking a row the instant one above "
 				+ "it collapses: the click on the still-moving row dies quietly (no surprise target), while rows "
 				+ "above the motion answer clicks throughout.",
-			render: () => html`<swg-live-list-churn></swg-live-list-churn>`,
+			render: () => html`<ykg-live-list-churn></ykg-live-list-churn>`,
 		},
 		{
 			caption: "The same churn in freeze mode (pointerSafety: freeze, the opt-in): rest the pointer inside "
 				+ "the list and geometry freezes -- ghosts hold and insertions wait invisibly until the pointer "
 				+ "leaves, and the deferred motion plays out one beat apart.  For surfaces that want quiescence "
 				+ "under the pointer; the price is a world that visibly waits for the mouse.",
-			render: () => html`<swg-live-list-churn pointerSafety="freeze"></swg-live-list-churn>`,
+			render: () => html`<ykg-live-list-churn pointerSafety="freeze"></ykg-live-list-churn>`,
 		},
 		{
 			caption: "Empty state: the owner provides the empty slot's content.",
 			render: () =>
-				html`<sw-live-list
+				html`<yk-live-list
 				.items=${[] as DemoRow[]}
 				.keyOf=${keyOf}
 				.renderItem=${renderRow}
-			><div slot="empty" style="color:var(--sw-text-faint);">queue empty (assignment may refill it)</div></sw-live-list>`,
+			><div slot="empty" style="color:var(--yk-text-faint);">queue empty (assignment may refill it)</div></yk-live-list>`,
 		},
 	],
 };
diff --git a/packages/yukey/src/yk-live-list.test.ts b/packages/yukey/src/yk-live-list.test.ts
index 2f00598..bdb92a3 100644
--- a/packages/yukey/src/yk-live-list.test.ts
+++ b/packages/yukey/src/yk-live-list.test.ts
@@ -1,6 +1,6 @@
 // @vitest-environment jsdom
 //
-// Tests for sw-live-list's megacolumn mode: light-DOM rendering, phase-dependent
+// Tests for yk-live-list's megacolumn mode: light-DOM rendering, phase-dependent
 // row identity stamping, the megacolumn-rescan announcement, the key-activity pin,
 // the two pointerSafety modes (click-guard's target-scoped swallow; freeze's
 // global hover pin + lockout), and the fail-closed contract checks.  The full
@@ -15,7 +15,7 @@
 import { html, type TemplateResult } from "lit";
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
 import { MegacolumnController } from "./megacolumn-controller.js";
-import type { LiveListPhase, SwLiveList } from "./yk-live-list.js";
+import type { LiveListPhase, YkLiveList } from "./yk-live-list.js";
 import "./yk-live-list.js";
 
 interface Item {
@@ -50,7 +50,7 @@ afterEach(() => {
 interface MegaRig {
 	root: HTMLElement;
 	container: HTMLElement;
-	list: SwLiveList<Item>;
+	list: YkLiveList<Item>;
 	ctl: MegacolumnController;
 	setItems: (next: Item[]) => Promise<void>;
 	key: (key: string) => KeyboardEvent;
@@ -64,7 +64,7 @@ async function makeMegaRig(initial: Item[]): Promise<MegaRig> {
 	const container = document.createElement("div");
 	container.setAttribute("data-mc-container", "");
 	root.appendChild(container);
-	const list = document.createElement("sw-live-list") as SwLiveList<Item>;
+	const list = document.createElement("yk-live-list") as YkLiveList<Item>;
 	list.setAttribute("megacolumn", "");
 	list.keyOf = keyOf;
 	list.renderItem = renderItem;
@@ -101,7 +101,7 @@ const fire = (el: Element, type: "click" | "mousedown"): void => {
 	el.dispatchEvent(new MouseEvent(type, { bubbles: true, composed: true, cancelable: true }));
 };
 
-const countItemClicks = (list: SwLiveList<Item>): () => number => {
+const countItemClicks = (list: YkLiveList<Item>): () => number => {
 	let n = 0;
 	list.addEventListener("item-click", () => {
 		n += 1;
@@ -332,7 +332,7 @@ describe("click-guard (the default pointerSafety): motion proceeds, stale clicks
 		await setItems(items("b"));
 		vi.advanceTimersByTime(list.holdMs); // collapse begins: motion in flight
 		await list.updateComplete;
-		expect(root.querySelector(".sw-ll-items")?.classList.contains("sw-ll-lockout")).toBe(false);
+		expect(root.querySelector(".yk-ll-items")?.classList.contains("yk-ll-lockout")).toBe(false);
 	});
 
 	it("a click on a row below a collapsing row is swallowed mid-motion, and lands after", async () => {
@@ -443,7 +443,7 @@ describe("click-guard (the default pointerSafety): motion proceeds, stale clicks
 	});
 
 	it("guards shadow-mode rows the same way (composed events cross the boundary)", async () => {
-		const list = document.createElement("sw-live-list") as SwLiveList<Item>;
+		const list = document.createElement("yk-live-list") as YkLiveList<Item>;
 		list.keyOf = keyOf;
 		list.renderItem = renderItem;
 		list.items = items("a", "b", "c");
@@ -498,7 +498,7 @@ describe("freeze mode (the opt-in): geometry holds still under the pointer", ()
 		await setItems(items("b")); // pointer outside: motion proceeds
 		vi.advanceTimersByTime(list.holdMs); // collapse begins
 		await list.updateComplete;
-		expect(root.querySelector(".sw-ll-items")?.classList.contains("sw-ll-lockout")).toBe(true);
+		expect(root.querySelector(".yk-ll-items")?.classList.contains("yk-ll-lockout")).toBe(true);
 	});
 
 	it("a focused ghost held by both pins survives the key pin decaying (hover holds on)", async () => {
@@ -588,7 +588,7 @@ describe("mega mode: fail closed", () => {
 
 	it("throws outside any [data-mc-container]", () => {
 		const reported = reportsFrom(() => {
-			const list = document.createElement("sw-live-list") as SwLiveList<Item>;
+			const list = document.createElement("yk-live-list") as YkLiveList<Item>;
 			list.setAttribute("megacolumn", "");
 			list.keyOf = keyOf;
 			list.renderItem = renderItem;
@@ -602,7 +602,7 @@ describe("mega mode: fail closed", () => {
 		container.setAttribute("data-mc-container", "");
 		document.body.appendChild(container);
 		const reported = reportsFrom(() => {
-			const list = document.createElement("sw-live-list") as SwLiveList<Item>;
+			const list = document.createElement("yk-live-list") as YkLiveList<Item>;
 			list.setAttribute("megacolumn", "");
 			list.innerHTML = `<div slot="empty">nope</div>`;
 			list.keyOf = keyOf;
@@ -628,7 +628,7 @@ describe("mega mode: fail closed", () => {
 
 describe("shadow mode regression (the pieces this stage touched)", () => {
 	it("renders in its own shadow root, no data-mc-row stamped, empty slot alive", async () => {
-		const list = document.createElement("sw-live-list") as SwLiveList<Item>;
+		const list = document.createElement("yk-live-list") as YkLiveList<Item>;
 		list.keyOf = keyOf;
 		list.renderItem = renderItem;
 		list.items = items("a");
@@ -647,7 +647,7 @@ describe("shadow mode regression (the pieces this stage touched)", () => {
 	});
 
 	it("renderEmpty wins over the slot in shadow mode too", async () => {
-		const list = document.createElement("sw-live-list") as SwLiveList<Item>;
+		const list = document.createElement("yk-live-list") as YkLiveList<Item>;
 		list.keyOf = keyOf;
 		list.renderItem = renderItem;
 		list.items = [];
diff --git a/packages/yukey/src/yk-live-list.ts b/packages/yukey/src/yk-live-list.ts
index a71a99e..849a2b5 100644
--- a/packages/yukey/src/yk-live-list.ts
+++ b/packages/yukey/src/yk-live-list.ts
@@ -1,4 +1,4 @@
-// sw-live-list: a keyed live list whose rows vanish safely under a pointer.
+// yk-live-list: a keyed live list whose rows vanish safely under a pointer.
 // Data replaces wholesale per tick (5Hz here); the owner supplies keying and
 // rendering, this component owns row lifecycle and click safety:
 //
@@ -65,7 +65,7 @@
 //     When a focused ghost collapses, the machine's vanished-row fallback moves
 //     focus and the controller's churn lockout covers the move.
 //   - everything INSIDE a row stays the owner's renderItem business:
-//     data-mc-main/aux/input, data-info, hotkey markings, sw-buttons.
+//     data-mc-main/aux/input, data-info, hotkey markings, yk-buttons.
 //
 // The keyboard's version of the pointer story is the KEY-ACTIVITY PIN (both
 // pointerSafety modes; the keyboard problem is the same in each): while keys
@@ -89,7 +89,7 @@
 // renderEmpty render-prop (which shadow mode also honors, over its default slot),
 // and itemStyles is refused (the owner's own stylesheet reaches light-DOM rows).
 // The row-lifecycle styles are adopted into the owner's root once, all their
-// class names sw-ll- prefixed so nothing collides in the shared tree.
+// class names yk-ll- prefixed so nothing collides in the shared tree.
 //
 // The row state machine lives here in component state, outside the template;
 // render() is a pure map over it (keyed repeat() keeps per-row DOM identity across
@@ -119,21 +119,21 @@ export interface ItemClickDetail<T> {
 }
 
 // The row-lifecycle styles, shared by both render modes.  Class names carry an
-// sw-ll- prefix because mega mode renders into the owner's tree, where bare
+// yk-ll- prefix because mega mode renders into the owner's tree, where bare
 // names like "closed" would be collision bait.
 const lifecycleStyles = css`
 	/* The lockout class only ever renders in freeze mode; click-guard swallows
 	   stale clicks per target instead of disabling the pointer wholesale. */
-	.sw-ll-items.sw-ll-lockout {
+	.yk-ll-items.yk-ll-lockout {
 		pointer-events: none;
 	}
-	.sw-ll-items:focus {
+	.yk-ll-items:focus {
 		outline: none;
 	}
-	.sw-ll-rowslot {
+	.yk-ll-rowslot {
 		display: grid;
 		grid-template-rows: 1fr;
-		transition: grid-template-rows var(--sw-live-list-collapse, 300ms) ease;
+		transition: grid-template-rows var(--yk-live-list-collapse, 300ms) ease;
 		/* The rowslot is pure geometry machinery and must be free to reach 0 height
 		   (closed rows, and both ends of the grow/collapse animations).  Owners set
 		   hit-target floors on their row CONTENT; in mega mode a [data-mc-row]
@@ -141,16 +141,16 @@ const lifecycleStyles = css`
 		   (entrances pop, collapses snap, pending rows hold blank space). */
 		min-height: 0;
 	}
-	.sw-ll-rowslot.sw-ll-grew {
-		animation: sw-live-list-enter var(--sw-live-list-enter, 300ms) ease;
+	.yk-ll-rowslot.yk-ll-grew {
+		animation: yk-live-list-enter var(--yk-live-list-enter, 300ms) ease;
 	}
-	.sw-ll-rowslot.sw-ll-closed {
+	.yk-ll-rowslot.yk-ll-closed {
 		grid-template-rows: 0fr;
 	}
-	.sw-ll-rowslot > .sw-ll-clip {
+	.yk-ll-rowslot > .yk-ll-clip {
 		overflow: hidden;
 	}
-	@keyframes sw-live-list-enter {
+	@keyframes yk-live-list-enter {
 		from {
 			grid-template-rows: 0fr;
 		}
@@ -159,7 +159,7 @@ const lifecycleStyles = css`
 	   semantics -- an announcement plus click safety -- not decoration), and the
 	   grace lockout still covers the snap (the JS side zeroes its durations too). */
 	@media (prefers-reduced-motion: reduce) {
-		.sw-ll-rowslot {
+		.yk-ll-rowslot {
 			transition: none;
 			animation: none;
 		}
@@ -169,7 +169,7 @@ const lifecycleStyles = css`
 // The mega-mode sheet: the lifecycle styles plus the host display rule that
 // :host would otherwise provide, adopted into the OWNER's root (once per root).
 const megaModeStyles = css`
-	sw-live-list {
+	yk-live-list {
 		display: block;
 	}
 	${lifecycleStyles}
@@ -217,8 +217,8 @@ interface RowState<T> {
 	timer: number | undefined; // the phase-advance timer; at most one per row
 }
 
-@customElement("sw-live-list")
-export class SwLiveList<T = unknown> extends LitElement {
+@customElement("yk-live-list")
+export class YkLiveList<T = unknown> extends LitElement {
 	static styles = [
 		css`
 			:host {
@@ -294,7 +294,7 @@ export class SwLiveList<T = unknown> extends LitElement {
 	private keyPinned = false;
 	private keyPinTimer: number | undefined;
 
-	@query(".sw-ll-items")
+	@query(".yk-ll-items")
 	private itemsEl?: HTMLElement;
 
 	constructor() {
@@ -305,7 +305,7 @@ export class SwLiveList<T = unknown> extends LitElement {
 		this.addEventListener("pointerenter", this.onPointerEnter);
 		this.addEventListener("pointerleave", this.onPointerLeave);
 		// The click guard intercepts in the CAPTURE phase on the host: it must beat
-		// both the row content's own listeners (an sw-button acting on a stale click)
+		// both the row content's own listeners (a yk-button acting on a stale click)
 		// and, in mega mode, the megacolumn controller's bubble-phase mousedown
 		// focus-sync on the ancestor container -- stopping propagation here covers
 		// every downstream consumer in one place, and the decision stays in the list
@@ -323,11 +323,11 @@ export class SwLiveList<T = unknown> extends LitElement {
 		this.mega = true;
 		if (this.children.length > 0) {
 			throw new Error(
-				"sw-live-list: megacolumn mode renders into the light DOM and has no slots; "
+				"yk-live-list: megacolumn mode renders into the light DOM and has no slots; "
 					+ "use the renderEmpty render-prop",
 			);
 		}
-		adoptIntoRoot(this.getRootNode(), megaModeStyles, "data-sw-live-list");
+		adoptIntoRoot(this.getRootNode(), megaModeStyles, "data-yk-live-list");
 		return this;
 	}
 
@@ -338,7 +338,7 @@ export class SwLiveList<T = unknown> extends LitElement {
 			// keydown a user aims anywhere in the column lands there first.
 			const container = this.closest<HTMLElement>("[data-mc-container]");
 			if (container === null) {
-				throw new Error("sw-live-list: megacolumn mode outside any [data-mc-container]");
+				throw new Error("yk-live-list: megacolumn mode outside any [data-mc-container]");
 			}
 			this.megaContainer = container;
 			container.addEventListener("keydown", this.onKeyActivity);
@@ -359,13 +359,13 @@ export class SwLiveList<T = unknown> extends LitElement {
 	override willUpdate(changed: PropertyValues<this>): void {
 		if (this.mega && this.itemStyles !== undefined) {
 			throw new Error(
-				"sw-live-list: itemStyles has no effect in megacolumn mode; "
+				"yk-live-list: itemStyles has no effect in megacolumn mode; "
 					+ "style rows from the owner's own stylesheet",
 			);
 		}
 		if (this.pointerSafety !== "click-guard" && this.pointerSafety !== "freeze") {
 			throw new Error(
-				`sw-live-list: unknown pointerSafety "${this.pointerSafety}" `
+				`yk-live-list: unknown pointerSafety "${this.pointerSafety}" `
 					+ "(the modes are \"click-guard\" and \"freeze\")",
 			);
 		}
@@ -380,7 +380,7 @@ export class SwLiveList<T = unknown> extends LitElement {
 
 	protected override updated(changed: PropertyValues<this>): void {
 		if (changed.has("itemStyles") && this.itemStyles !== undefined && this.shadowRoot !== null) {
-			const own = (this.constructor as typeof SwLiveList).elementStyles;
+			const own = (this.constructor as typeof YkLiveList).elementStyles;
 			adoptStyles(this.shadowRoot, [...own, this.itemStyles]);
 		}
 		// Mega mode: any completed render may have changed the row set under the
@@ -512,7 +512,7 @@ export class SwLiveList<T = unknown> extends LitElement {
 		// If real DOM focus is inside the row being dropped, move it out rather than
 		// letting it die silently: to the mega container (the column's one focusable)
 		// in mega mode, else to the list's own item container.
-		const slot = this.renderRoot.querySelector(`.sw-ll-rowslot[data-key="${CSS.escape(row.key)}"]`);
+		const slot = this.renderRoot.querySelector(`.yk-ll-rowslot[data-key="${CSS.escape(row.key)}"]`);
 		const focusRoot = this.shadowRoot ?? (this.getRootNode() as Document | ShadowRoot);
 		const active = focusRoot.activeElement;
 		if (slot !== null && active !== null && slot.contains(active)) {
@@ -612,7 +612,7 @@ export class SwLiveList<T = unknown> extends LitElement {
 		if (this.frozen) return; // freeze mode's lockout owns this story instead
 		const target = e.composedPath()[0];
 		if (!(target instanceof Element)) return;
-		const slot = target.closest<HTMLElement>(".sw-ll-rowslot");
+		const slot = target.closest<HTMLElement>(".yk-ll-rowslot");
 		if (slot === null) return;
 		const key = slot.dataset.key;
 		if (key === undefined || !this.isMovingTarget(key)) return;
@@ -721,7 +721,7 @@ export class SwLiveList<T = unknown> extends LitElement {
 	// Read from the DOM rather than via aria-activedescendant so this stays
 	// independent of the controller's id-derivation rules.
 	private isRowFocused(row: RowState<T>): boolean {
-		const slot = this.renderRoot.querySelector(`.sw-ll-rowslot[data-key="${CSS.escape(row.key)}"]`);
+		const slot = this.renderRoot.querySelector(`.yk-ll-rowslot[data-key="${CSS.escape(row.key)}"]`);
 		return slot?.classList.contains("mc-row-focused") ?? false;
 	}
 
@@ -761,13 +761,13 @@ export class SwLiveList<T = unknown> extends LitElement {
 	override render(): TemplateResult {
 		const rowList = [...this.rows.values()];
 		const anyVisible = rowList.some((r) => r.phase !== "pending");
-		const vars = `--sw-live-list-enter: ${this.enterMs}ms; --sw-live-list-collapse: ${this.collapseMs}ms;`;
+		const vars = `--yk-live-list-enter: ${this.enterMs}ms; --yk-live-list-collapse: ${this.collapseMs}ms;`;
 		const empty = anyVisible
 			? nothing
 			: (this.renderEmpty?.() ?? (this.mega ? nothing : html`<slot name="empty"></slot>`));
 		return html`
 			<div
-				class=${classMap({ "sw-ll-items": true, "sw-ll-lockout": this.frozen && this.locked })}
+				class=${classMap({ "yk-ll-items": true, "yk-ll-lockout": this.frozen && this.locked })}
 				style=${vars}
 				tabindex="-1"
 			>
@@ -794,13 +794,13 @@ export class SwLiveList<T = unknown> extends LitElement {
 		// its focus classes on these elements imperatively, and an interpolated class
 		// attribute would wipe them on the next content tick.
 		return html`<div
-			class=${classMap({ "sw-ll-rowslot": true, "sw-ll-closed": closed, "sw-ll-grew": row.grew })}
+			class=${classMap({ "yk-ll-rowslot": true, "yk-ll-closed": closed, "yk-ll-grew": row.grew })}
 			data-key=${row.key}
 			data-mc-row=${this.mega ? row.key : nothing}
 			aria-hidden=${closed ? "true" : nothing}
 			@click=${() => this.onRowClick(row)}
 		>
-			<div class="sw-ll-clip">${this.renderItem(row.item, phase)}</div>
+			<div class="yk-ll-clip">${this.renderItem(row.item, phase)}</div>
 		</div>`;
 	}
 
@@ -821,6 +821,6 @@ export class SwLiveList<T = unknown> extends LitElement {
 
 declare global {
 	interface HTMLElementTagNameMap {
-		"sw-live-list": SwLiveList;
+		"yk-live-list": YkLiveList;
 	}
 }
diff --git a/packages/yukey/static/gallery.html b/packages/yukey/static/gallery.html
index bbbd4b2..e8ddbd9 100644
--- a/packages/yukey/static/gallery.html
+++ b/packages/yukey/static/gallery.html
@@ -12,13 +12,13 @@
 	}
 	/* Height only: display comes from the component's own :host rule
 	   (a document rule here would override it and break the shell's grid). */
-	sw-gallery {
+	yk-gallery {
 		height: 100%;
 	}
 </style>
 </head>
 <body>
-<sw-gallery></sw-gallery>
+<yk-gallery></yk-gallery>
 <script type="module" src="/gallery.js"></script>
 </body>
 </html>