diff --git a/examples/apps/img/icons/adforge.png b/examples/apps/img/icons/adforge.png new file mode 100644 index 0000000..0bba50e Binary files /dev/null and b/examples/apps/img/icons/adforge.png differ diff --git a/examples/apps/img/icons/adgen.png b/examples/apps/img/icons/adgen.png new file mode 100644 index 0000000..b65b999 Binary files /dev/null and b/examples/apps/img/icons/adgen.png differ diff --git a/examples/apps/img/icons/adpulse.png b/examples/apps/img/icons/adpulse.png new file mode 100644 index 0000000..9f26af7 Binary files /dev/null and b/examples/apps/img/icons/adpulse.png differ diff --git a/examples/apps/img/icons/aplus.png b/examples/apps/img/icons/aplus.png new file mode 100644 index 0000000..2a43342 Binary files /dev/null and b/examples/apps/img/icons/aplus.png differ diff --git a/examples/apps/img/icons/arcana.png b/examples/apps/img/icons/arcana.png new file mode 100644 index 0000000..707e97a Binary files /dev/null and b/examples/apps/img/icons/arcana.png differ diff --git a/examples/apps/img/icons/bank.png b/examples/apps/img/icons/bank.png new file mode 100644 index 0000000..18b02d0 Binary files /dev/null and b/examples/apps/img/icons/bank.png differ diff --git a/examples/apps/img/icons/batch.png b/examples/apps/img/icons/batch.png new file mode 100644 index 0000000..40a7aca Binary files /dev/null and b/examples/apps/img/icons/batch.png differ diff --git a/examples/apps/img/icons/brandbrain.png b/examples/apps/img/icons/brandbrain.png new file mode 100644 index 0000000..87b1201 Binary files /dev/null and b/examples/apps/img/icons/brandbrain.png differ diff --git a/examples/apps/img/icons/capp.png b/examples/apps/img/icons/capp.png new file mode 100644 index 0000000..f6fba0f Binary files /dev/null and b/examples/apps/img/icons/capp.png differ diff --git a/examples/apps/img/icons/cartridge.png b/examples/apps/img/icons/cartridge.png new file mode 100644 index 0000000..4322e50 Binary files /dev/null and b/examples/apps/img/icons/cartridge.png differ diff --git a/examples/apps/img/icons/cast.png b/examples/apps/img/icons/cast.png new file mode 100644 index 0000000..9619394 Binary files /dev/null and b/examples/apps/img/icons/cast.png differ diff --git a/examples/apps/img/icons/feature.png b/examples/apps/img/icons/feature.png new file mode 100644 index 0000000..353aa61 Binary files /dev/null and b/examples/apps/img/icons/feature.png differ diff --git a/examples/apps/img/icons/hardware.png b/examples/apps/img/icons/hardware.png new file mode 100644 index 0000000..1506832 Binary files /dev/null and b/examples/apps/img/icons/hardware.png differ diff --git a/examples/apps/img/icons/huddle.png b/examples/apps/img/icons/huddle.png new file mode 100644 index 0000000..8f2b8c8 Binary files /dev/null and b/examples/apps/img/icons/huddle.png differ diff --git a/examples/apps/img/icons/ideabrain.png b/examples/apps/img/icons/ideabrain.png new file mode 100644 index 0000000..f9bf530 Binary files /dev/null and b/examples/apps/img/icons/ideabrain.png differ diff --git a/examples/apps/img/icons/identity.png b/examples/apps/img/icons/identity.png new file mode 100644 index 0000000..14042a2 Binary files /dev/null and b/examples/apps/img/icons/identity.png differ diff --git a/examples/apps/img/icons/marquee.png b/examples/apps/img/icons/marquee.png new file mode 100644 index 0000000..8cedd99 Binary files /dev/null and b/examples/apps/img/icons/marquee.png differ diff --git a/examples/apps/img/icons/mkt.png b/examples/apps/img/icons/mkt.png new file mode 100644 index 0000000..d2c55dc Binary files /dev/null and b/examples/apps/img/icons/mkt.png differ diff --git a/examples/apps/img/icons/natal.png b/examples/apps/img/icons/natal.png new file mode 100644 index 0000000..67f81bf Binary files /dev/null and b/examples/apps/img/icons/natal.png differ diff --git a/examples/apps/img/icons/prism.png b/examples/apps/img/icons/prism.png new file mode 100644 index 0000000..89e0410 Binary files /dev/null and b/examples/apps/img/icons/prism.png differ diff --git a/examples/apps/img/icons/redline.png b/examples/apps/img/icons/redline.png new file mode 100644 index 0000000..be02167 Binary files /dev/null and b/examples/apps/img/icons/redline.png differ diff --git a/examples/apps/img/icons/reel.png b/examples/apps/img/icons/reel.png new file mode 100644 index 0000000..acc263d Binary files /dev/null and b/examples/apps/img/icons/reel.png differ diff --git a/examples/apps/img/icons/retail.png b/examples/apps/img/icons/retail.png new file mode 100644 index 0000000..12c0ce1 Binary files /dev/null and b/examples/apps/img/icons/retail.png differ diff --git a/examples/apps/img/icons/saas.png b/examples/apps/img/icons/saas.png new file mode 100644 index 0000000..6cba8f8 Binary files /dev/null and b/examples/apps/img/icons/saas.png differ diff --git a/examples/apps/img/icons/shelf.png b/examples/apps/img/icons/shelf.png new file mode 100644 index 0000000..a9eaee3 Binary files /dev/null and b/examples/apps/img/icons/shelf.png differ diff --git a/examples/apps/img/icons/studio.png b/examples/apps/img/icons/studio.png new file mode 100644 index 0000000..280737b Binary files /dev/null and b/examples/apps/img/icons/studio.png differ diff --git a/examples/apps/img/icons/take.png b/examples/apps/img/icons/take.png new file mode 100644 index 0000000..1d78004 Binary files /dev/null and b/examples/apps/img/icons/take.png differ diff --git a/examples/apps/index.html b/examples/apps/index.html index 236dfce..bc88b61 100644 --- a/examples/apps/index.html +++ b/examples/apps/index.html @@ -340,8 +340,11 @@ .hero-h .hl{color:var(--ink-2)} .hero-sub{color:var(--ink-2);max-width:64ch;margin:18px 0 24px;font-size:15px;line-height:1.62} .hero-sub b{color:var(--ink);font-weight:600} + .hero-ctas{display:flex;align-items:center;gap:11px;flex-wrap:wrap;margin-top:4px} .hero-cta{display:inline-flex;align-items:center;gap:9px;background:var(--accent);color:var(--accent-ink);border:0;border-radius:var(--r-pill);padding:13px 20px;font:600 14px/1 var(--font-sans);cursor:pointer} .hero-cta:hover{background:var(--accent-hover)} + .hero-cta.ghost{background:transparent;color:var(--ink);box-shadow:inset 0 0 0 1px var(--line-2)} + .hero-cta.ghost:hover{background:var(--card);box-shadow:inset 0 0 0 1px var(--faint)} .way{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin:16px 0 4px} .step{position:relative;background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:16px 16px;cursor:pointer;transition:transform .12s,border-color .12s} @@ -390,8 +393,27 @@ background:radial-gradient(130% 120% at 50% -10%, color-mix(in srgb, var(--fam,#2E8B6A) 26%, transparent), transparent 60%)} .thumb::after{content:"";position:absolute;inset:0;box-shadow:0 1px 0 rgba(255,255,255,.03) inset;pointer-events:none;border-radius:inherit;z-index:2} - /* ============================ RECENTLY ADDED (icons list) ============================ */ - #recent-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px 28px} + /* ============================ NEW ADDITIONS (compact directory strip) ============================ */ + .na-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:40px 0 22px} + .na-title{margin:0;font-family:var(--font-display);font-size:22px;font-weight:700;letter-spacing:-.025em} + .na-nav{display:flex;gap:8px} + .na-btn{width:33px;height:33px;border-radius:9px;border:1px solid var(--line);background:var(--card);color:var(--ink-2); + cursor:pointer;display:grid;place-items:center;font-size:17px;line-height:1;transition:border-color .12s,color .12s,background .12s} + .na-btn:hover{border-color:var(--line-2);color:var(--ink)} + .na-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:20px 40px} + .na-item{display:flex;align-items:center;gap:14px;min-width:0;padding:4px 2px;border-radius:12px;transition:transform .12s} + .na-item:hover{transform:translateY(-1px)} + .na-item .ic{flex:none} + .na-tx{min-width:0} + .na-n{display:flex;align-items:center;gap:7px;font-family:var(--font-display);font-size:15px;font-weight:600;letter-spacing:-.01em;color:var(--ink)} + .na-n .verify{width:12px;height:12px} + .na-item:hover .na-n{color:var(--accent-hover)} + .na-c{display:block;font-size:13px;color:var(--faint);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} + @media(max-width:1100px){ .na-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 28px} } + @media(max-width:560px){ .na-grid{grid-template-columns:minmax(0,1fr)} } + + /* ============================ RECENTLY ADDED (legacy icons list) ============================ */ + #recent-list.legacy-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px 28px} .recent-row{display:flex;align-items:center;gap:12px;padding:11px 6px;border-bottom:1px solid var(--line);min-width:0} .recent-row:hover{background:var(--card)} .recent-row .ic{width:34px;height:34px;border-radius:var(--r-sm);flex:none} @@ -419,6 +441,9 @@ .cat-tag{display:inline-block;font:600 9px/1 var(--font-sans);letter-spacing:.06em;text-transform:uppercase;color:var(--faint);border:1px solid var(--line);border-radius:5px;padding:4px 7px} .pro-pill{font:600 8.5px/1 var(--font-sans);letter-spacing:.1em;text-transform:uppercase;color:var(--sim);background:var(--sim-bg);border:1px solid rgba(200,162,75,.28);border-radius:5px;padding:4px 7px} .ic{display:grid;place-items:center} + /* real generated icon PNGs — trimmed to the tile, so object-fit fills the rounded box */ + .ic-img{overflow:hidden;background:transparent;box-shadow:0 2px 7px -3px rgba(0,0,0,.6)} + .ic-img img{width:100%;height:100%;object-fit:cover;display:block} .cost{display:block;margin-top:8px;font:500 9.5px/1 var(--font-mono);color:var(--faint);font-variant-numeric:tabular-nums; white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%} @@ -609,20 +634,44 @@ .hero-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,500px);gap:46px;align-items:center} .hero-copy{min-width:0} .hero-art{display:grid;grid-template-columns:1fr 1fr;gap:12px;min-width:0} - .hero-art a{display:block;background:var(--card);border:1px solid var(--line);border-radius:var(--r); + .hero-art:not(.spotlight) a{display:block;background:var(--card);border:1px solid var(--line);border-radius:var(--r); overflow:hidden;box-shadow:var(--shadow);transition:transform .14s,border-color .14s} - .hero-art a:hover{transform:translateY(-3px);border-color:var(--line-2)} - .hero-art .thumb{border-radius:0} - .hero-art .cap{display:flex;align-items:center;gap:8px;padding:9px 11px} - .hero-art .cap .ic{width:22px;height:22px;border-radius:7px;flex:none} - .hero-art .cap .ic svg{width:56%;height:56%} - .hero-art .cap .nm{font-family:var(--font-display);font-size:12.5px;font-weight:600;letter-spacing:-.01em; + .hero-art:not(.spotlight) a:hover{transform:translateY(-3px);border-color:var(--line-2)} + .hero-art:not(.spotlight) .thumb{border-radius:0} + .hero-art:not(.spotlight) .cap{display:flex;align-items:center;gap:8px;padding:9px 11px} + .hero-art:not(.spotlight) .cap .ic{width:22px;height:22px;border-radius:7px;flex:none} + .hero-art:not(.spotlight) .cap .ic svg{width:56%;height:56%} + .hero-art:not(.spotlight) .cap .nm{font-family:var(--font-display);font-size:12.5px;font-weight:600;letter-spacing:-.01em; overflow:hidden;text-overflow:ellipsis;white-space:nowrap} @media(max-width:1180px){ .hero-row{grid-template-columns:minmax(0,1fr);gap:34px} .hero-art{grid-template-columns:repeat(4,1fr)} } @media(max-width:640px){ .hero-art{grid-template-columns:1fr 1fr} } + /* hero SPOTLIGHT — one big brandbrain preview is the clearest "start here" on the page */ + .hero-art.spotlight{display:block} + @media(max-width:1180px){ .hero-art.spotlight{max-width:560px} } + .hero-art.spotlight a.hero-star{position:relative;display:block;background:var(--card);border:1px solid var(--line); + border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--shadow);transition:transform .14s,border-color .14s,box-shadow .14s} + .hero-art.spotlight a.hero-star:hover{transform:translateY(-3px);border-color:var(--line-2);box-shadow:var(--shadow-pop)} + .hero-art.spotlight .hero-star .thumb{border-radius:0;aspect-ratio:16/10} + .hs-badge{position:absolute;top:12px;left:12px;z-index:3;font:600 9.5px/1 var(--font-sans);letter-spacing:.13em; + text-transform:uppercase;color:var(--accent-ink);background:var(--accent);border-radius:var(--r-pill);padding:6px 11px} + .hs-cap{display:flex;align-items:center;gap:12px;padding:13px 15px} + .hs-cap .ic{flex:none} + .hs-t{min-width:0;flex:1} + .hs-n{display:flex;align-items:center;gap:7px;font-family:var(--font-display);font-size:16px;font-weight:600;letter-spacing:-.01em} + .hs-d{display:block;font-size:12.5px;color:var(--ink-2);margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} + .hs-go{flex:none;font:600 12.5px/1 var(--font-sans);color:var(--ink);background:var(--card-2);border:1px solid var(--line-2); + border-radius:var(--r-pill);padding:8px 13px;transition:background .14s,color .14s,border-color .14s} + .hero-star:hover .hs-go{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)} + .hero-next{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap} + .hn-chip{display:inline-flex;align-items:center;gap:8px;background:var(--card);border:1px solid var(--line); + border-radius:var(--r-pill);padding:6px 13px 6px 6px;transition:border-color .14s,transform .14s} + .hn-chip:hover{border-color:var(--line-2);transform:translateY(-2px)} + .hn-chip .ic{flex:none} + .hn-n{font:600 12px/1 var(--font-sans);color:var(--ink-2)} + .hn-chip:hover .hn-n{color:var(--ink)} /* the why cards reuse .card/.grid; only the old/new contrast pair is new */ .why-grid{grid-template-columns:repeat(3,minmax(0,1fr))} .card.why{cursor:default} @@ -836,8 +885,11 @@

Open source · runs on the Claude you already pay for

One connection. Not one per app.

Every AI app asks you to start over — a new account, a new subscription, a new copy of everything you've already explained. Switchboard sits in between: you connect your own Claude once, and all 27 apps here run on that one connection. Nothing you type reaches a server of ours.

- -

Free. No account. Takes about two minutes.

+
+ Try brandbrain + +
+

Free · runs on the Claude you already pay for · no account. brandbrain is the one to start with.

@@ -851,28 +903,6 @@

One connection. Not one per app.

- -
-
One connection

Three things you set up once, instead of once per app.

what every other AI app makes you repeat — and what happens here instead
-
-
-

One account

-

Everywhere else: every app is another signup, another password, another company holding a login.

-

Here: no account with us, and none with any app. You bring the Claude account you already have.

-
-
-

One subscription

-

Everywhere else: their model, their key, their markup — a second bill stacked on the AI you already pay for.

-

Here: your Claude is already paid for and sitting idle. Apps spend that quota, and we never touch the transaction.

-
-
-

One copy of your context

-

Everywhere else: each app learns your business from scratch, so you explain it again for the third time.

-

Here: your context lives in a folder on your machine. You lend it to an app for one job, and it hands it back.

-
-
-
-
The board

27 wrapps, and every tile is the real thing.

Not screenshots. Each tile is that wrapp's own page, rendered live inside this one. Open one and it runs on your Claude.
-

To be exact: each tile is the app's real landing page loading live in this page. It is a live preview of a real app, not the app doing your work — that starts when you open one.

-

Every wrapp on the board today is built by us. Third-party wrapps will run inside an isolated runner that cannot phone home — that runner is designed and not yet built, which is exactly why the board is first-party today.

- -
-

Six of these are ideabrain opened to one kind of idea, not six separate apps. They get their own tile because they are their own front door, and we say it here because you would work it out in three clicks anyway.

- -

The first three cords

-
- -

brandbrain

Consumer brands, built on receipts — white space, every decision locked, launch. On your Claude.

-
- -

ideabrain

Not a company yet? Research, a playbook for your kind of idea, a deck, and who to reach out to.

-
- -

Bank

Notes, tasks and [[links]] in plain .md you own — a second brain you can actually ask.

-
+ +
+

New Additions

+
+ + + +
+
-

Newest on the board

-
- - +
-
The catalog

All wrapps

tools that run on your own AI — free
+

All Tools

every tile is the live app — open one and it runs on your own Claude, free

Brand & content

brand systems, the knowledge bank, copy review
@@ -1021,6 +1039,29 @@

One copy of your context

+ +
+
One connection

Three things you set up once, instead of once per app.

what every other AI app makes you repeat — and what happens here instead
+
+
+

One account

+

Everywhere else: every app is another signup, another password, another company holding a login.

+

Here: no account with us, and none with any app. You bring the Claude account you already have.

+
+
+

One subscription

+

Everywhere else: their model, their key, their markup — a second bill stacked on the AI you already pay for.

+

Here: your Claude is already paid for and sitting idle. Apps spend that quota, and we never touch the transaction.

+
+
+

One copy of your context

+

Everywhere else: each app learns your business from scratch, so you explain it again for the third time.

+

Here: your context lives in a folder on your machine. You lend it to an app for one job, and it hands it back.

+
+
+
+ diff --git a/examples/apps/src/home.js b/examples/apps/src/home.js index a92cf57..b82be3a 100644 --- a/examples/apps/src/home.js +++ b/examples/apps/src/home.js @@ -67,11 +67,6 @@ const isDemoEmpty = isDemo && new URLSearchParams(location.search).get("demo") = // hero speaks instead — the same rule the rest of this file follows: never invent a count. const demoTasks = isDemo && !isDemoEmpty; -// The newest arrivals — the founder-mandated icons list. Curated newest-first (the in-page ./ wrapps -// are the latest builds); each row carries the wrapp glyph + a category one-liner. -// Ids must exist in APPS — renderRecent() silently skips unknown ones, so a stale entry -// here ships a list whose length quietly lies. ("chat" lived here until it left the catalog.) -const RECENTLY_ADDED = ["huddle", "reel", "identity", "take", "batch", "marquee", "redline"]; // ---------- WHAT'S NEXT / TASKS — the intent bar + the one shared task list ---------- // The store writes tasks into the SAME file + dialect Bank writes (bank.js:29,75-89,760-777), so a @@ -442,26 +437,46 @@ function renderBoardCounts() { } } -// The hero's right column: four real apps, previewed live. Same makeThumb() the catalog uses, -// so the first screen shows the product instead of describing it. Chosen for visual variety -// across families rather than curated by hand — ids are checked so a catalog edit can't 404. -const HERO_ART = ["brandbrain", "adforge", "arcana", "bank"]; +// The hero's right column is a SPOTLIGHT, not a wall of equal tiles: one big live brandbrain +// preview badged "Start here", because the whole store is worthless if a first-time visitor +// doesn't know which app to open first. Clicking it goes straight to brandbrain (the try, not the +// tour). Under it, a thin "popular next" row keeps the other flagships one tap away. +const HERO_STAR = "brandbrain"; +const HERO_NEXT = ["adforge", "bank", "redline"]; function renderHeroArt() { const box = $("hero-art"); if (!box) return; box.textContent = ""; - for (const id of HERO_ART) { + const star = APP_BY_ID[HERO_STAR]; + if (!star) return; + box.classList.add("spotlight"); + + const a = mk("a", "hero-star"); + a.href = star.href; // straight to the app — this tile IS the "try it" + if (/^https:/.test(star.href)) { a.target = "_blank"; a.rel = "noreferrer"; } + a.title = `Try ${star.name}`; + a.dataset.app = star.id; + const cap = mk("span", "hs-cap"); + const t = mk("span", "hs-t"); + const nm = mk("span", "hs-n"); + nm.append(document.createTextNode(star.name)); + if (isVerified(star)) nm.insertAdjacentHTML("beforeend", verifyBadge()); + t.append(nm, mk("span", "hs-d", "Consumer brands, built on receipts — on your own Claude.")); + cap.append(glyphTile(star.id, 40), t, mk("span", "hs-go", "Try it →")); + a.append(makeThumb(star, true), mk("span", "hs-badge", "Start here"), cap); + box.append(a); + + const row = mk("div", "hero-next"); + for (const id of HERO_NEXT) { const app = APP_BY_ID[id]; if (!app) continue; - const a = mk("a"); - a.href = detailHref(app.id); - a.title = app.name; - a.dataset.app = app.id; - const cap = mk("span", "cap"); - cap.append(glyphTile(app.id, 22), mk("span", "nm", app.name)); - a.append(makeThumb(app, true), cap); - box.append(a); + const chip = mk("a", "hn-chip"); + pointAtDetail(chip, app); // these are "see the wrapp", not "open it" + chip.title = app.name; + chip.append(glyphTile(app.id, 26), mk("span", "hn-n", app.name)); + row.append(chip); } + box.append(row); } // The designation strip over the board — same scroll-to-section behaviour as the sidebar @@ -489,31 +504,48 @@ function renderBoardStrip() { // ======================================================================================== -// RECENTLY ADDED — the icons list (founder-mandated glyphs) +// NEW ADDITIONS — the compact directory strip: 12 icon rows (icon + name + category), +// a rotating window over a larger pool driven by the ‹ + › head controls. This is the first +// browsable section under the hero — the "get me straight to the tools" surface. // ======================================================================================== +// The pool the strip rotates through. Broader than the visible 12 so ‹ › actually turns up +// fresh tiles; every id is checked against APPS so a catalog edit can't 404 a row. +const NA_POOL = [ + "redline", "marquee", "batch", "take", "identity", "reel", "huddle", "cast", "prism", "adgen", + "arcana", "natal", "cartridge", "shelf", "studio", "aplus", "adforge", "adpulse", "mkt", "capp", +].filter((id) => APP_BY_ID[id]); +const NA_SHOWN = 12; +let naStart = 0; function renderRecent() { const box = $("recent-list"); + if (!box) return; box.textContent = ""; - for (const id of RECENTLY_ADDED) { + const n = Math.min(NA_SHOWN, NA_POOL.length); + for (let i = 0; i < n; i++) { + const id = NA_POOL[(naStart + i) % NA_POOL.length]; const app = APP_BY_ID[id]; if (!app) continue; - const row = mk("a", "recent-row"); + const row = mk("a", "na-item"); row.dataset.app = id; pointAtDetail(row, app); - row.append(glyphTile(id, 34)); - const t = mk("span", "rr-t"); - const n = mk("span", "rr-n"); - n.append(document.createTextNode(app.name)); - if (isVerified(app)) n.insertAdjacentHTML("beforeend", verifyBadge()); - t.append(n, mk("span", "rr-c", firstLine(app))); - row.append(t, mk("span", "rr-cat", categoryOf(id))); + row.append(glyphTile(id, 44)); + const t = mk("span", "na-tx"); + const nm = mk("span", "na-n"); + nm.append(document.createTextNode(app.name)); + if (isVerified(app)) nm.insertAdjacentHTML("beforeend", verifyBadge()); + t.append(nm, mk("span", "na-c", categoryOf(id))); + row.append(t); box.append(row); } } -function firstLine(app) { - const card = document.querySelector(`a.card[data-app="${app.id}"] p`); - return card ? card.textContent : categoryOf(app.id); +function turnNewAdditions(dir) { + const len = NA_POOL.length || 1; + naStart = (naStart + dir * 4 + len) % len; + renderRecent(); } +$("na-prev")?.addEventListener("click", () => turnNewAdditions(-1)); +$("na-next")?.addEventListener("click", () => turnNewAdditions(1)); +$("na-add")?.addEventListener("click", () => $("store")?.scrollIntoView({ behavior: "smooth", block: "start" })); // ======================================================================================== // THE WAY — onboarding stepper (reflects reality, routes the click to the next move) @@ -761,6 +793,7 @@ function onDisconnected() { function showDash() { $("hero").hidden = true; if ($("pitch")) $("pitch").hidden = true; // the pitch is for strangers, not for your dashboard + if ($("why-sec")) $("why-sec").hidden = true; // ditto — the "why one connection" cards left the hero $("dash").hidden = false; $("wallet-chip").hidden = false; $("dock").hidden = false; @@ -776,6 +809,7 @@ function hideDash() { promotedAction = null; $("hero").hidden = false; if ($("pitch")) $("pitch").hidden = false; + if ($("why-sec")) $("why-sec").hidden = false; $("wallet-chip").hidden = true; $("dock").hidden = true; document.body.classList.remove("plan-pro", "is-demo"); diff --git a/examples/apps/src/store/glyphs.js b/examples/apps/src/store/glyphs.js index 6a2caaf..f16575e 100644 --- a/examples/apps/src/store/glyphs.js +++ b/examples/apps/src/store/glyphs.js @@ -131,12 +131,37 @@ const TILE = { /** The saturated icon colour for a catalog id (per-app, with a family fallback). */ export function tileColor(id) { return TILE[id] || FAM_TILE[entry(id).fam] || "#12A594"; } -/** A rounded app-icon tile: saturated per-app fill + white glyph + soft top-light. Cards, dock, list. */ +// ---- The real icon set. One finished PNG per id in ./img/icons/.png — a matte gradient tile +// with a white two-tone symbol, generated as a cohesive family and trimmed to the tile bounds. Any +// id NOT listed here (parked / one-off wrapps without a made icon yet) falls back to the inline +// gradient glyph tile below, so the store never renders a broken image. ---- +export const ICON_IDS = new Set([ + "brandbrain", "ideabrain", "bank", "mkt", "capp", "saas", "retail", "hardware", "feature", + "adpulse", "adforge", "shelf", "studio", "aplus", "batch", "take", "identity", "reel", "marquee", + "huddle", "natal", "arcana", "redline", "cartridge", "cast", "prism", "adgen", +]); +export const hasIcon = (id) => ICON_IDS.has(id); +export const iconSrc = (id) => `./img/icons/${id}.png`; + +/** A rounded app-icon tile. A real generated icon PNG when we have one; otherwise the inline + * gradient glyph tile (parked wrapps). Radius scales with size so it reads as one icon at any size. */ export function glyphTile(id, size = 34) { - const c = tileColor(id); const s = document.createElement("span"); s.className = "ic"; s.style.width = s.style.height = size + "px"; + s.style.borderRadius = Math.max(6, Math.round(size * 0.235)) + "px"; + if (ICON_IDS.has(id)) { + s.classList.add("ic-img"); + const img = document.createElement("img"); + img.src = iconSrc(id); + img.alt = ""; + img.loading = "lazy"; + img.decoding = "async"; + img.draggable = false; + s.appendChild(img); + return s; + } + const c = tileColor(id); s.style.background = `linear-gradient(155deg, color-mix(in srgb, ${c} 76%, #fff 24%), ${c} 74%)`; s.style.color = "#fff"; s.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,.34), inset 0 0 0 1px rgba(255,255,255,.10), 0 3px 8px -3px rgba(0,0,0,.6)";