---
name: making-the-hero
description: "DEV skill: the COMPLETE recipe for the nb-bridge hero image - build_nb_hero.py in this folder renders the whole 2000x1250 from a fresh real-nb-window capture, then hero-studio full-import stamps provenance at the human gate. Read this BEFORE touching the hero. Encodes every rule John set shipping v10 (2026-08-09) and every trap: the studio's .window wrapper, font-metric lies, occlusion-blank captures, the title-vs-headline split. Sibling recipe: pup-bridge dev-skills/pup-hero-image (we share its design constants)."
---
Parent skill: **adom-native-browser** (maintainers). Publish flow: **publishing-the-bridge**.

# The nb-bridge hero: build, gate, apply

Shipped as hero-v10 after ~6 hours of iteration with John at the gate. EVERYTHING below was paid
for; do not re-derive it.

## The pipeline (the only path that works)

```bash
# 1. a REAL nb window - never pup (pup is the sibling product; John rejected a pup window hard)
adom-bridge-cli --target <box> --ai-thread "<you>" nbrowser_wake_profile '{"profile":"chrome:john@adom.inc"}'
adom-bridge-cli ... nbrowser_open_window '{"sessionId":"nbhero","thread":"<you>","purpose":"hero capture",
  "profile":"chrome:john@adom.inc","url":"https://wiki.adom.inc/blob/app/nb-bridge/demo/nb.html"}'
adom-bridge-cli ... nbrowser_taskbar '{"sessionId":"nbhero","overlay":{"badge":"adom"}}'   # the nb signature
adom-bridge-cli ... nbrowser_window_state '{"sessionId":"nbhero","width":900,"height":772}' # taller aspect -> vitrine fills pup's height
adom-bridge-cli ... desktop_screenshot_window '{"hwnd":<from open>}'                        # RAW - keep the pink debugging banner!

# 2. build the full 2000x1250 (fonts/logo/taskbar-strip ship in assets/ - reproducible from a bare clone)
python3 dev-skills/making-the-hero/build_nb_hero.py --window <that capture> --out /tmp/hero_candidate.png

# 3. human gate: full-import (NEVER the studio's native vitrine slot - see traps)
HERO_STUDIO_PORT=<free> hero-studio pose adom/nb-bridge --type bridge --port <free> &   # --surface pup is broken (#31); open the gate in pup yourself
hero-studio vitrine --file /tmp/hero_candidate.png --full --port <port>
hero-studio set overlay.pill=false overlay.slug=false --port <port>                     # our pill is baked; studio double-stamps otherwise
# open the proxy URL in a pup window for John, maximize ONCE, flash - never foreground uninvited
# poll GET /state .approved; a timed-out pose process does NOT kill the server

# 4. apply (order matters)
cp <generated png> screenshots/hero.png; cp <generated png> screenshots/hero-vN.png
adom-wiki repo push adom/nb-bridge --files screenshots/hero.png screenshots/hero-vN.png -m "..."
adom-wiki page hero adom/nb-bridge --image screenshots/hero.png     # works for us (pup's skill says broken for them - path layout differs)
adom-wiki repo push adom/nb-bridge --files page.json -m "..."       # LAST - pkg publish clobbers title+hero
# verify: hero_provenance=="studio" AND sha256(served blob) == sha256(generated) - then cache-busted refresh in John's pup window
```

## John's rules (each cost a rejected round - all verbatim-enforced in the build script)

1. **Real nb window, never pup.** The vitrine window is opened by nbrowser_* in a real signed-in
   profile. The Work-profile pill + avatar + the pink "Adom Native Browser Extension started
   debugging this browser" banner ARE the product story - do not splice, retouch, or re-composite
   the capture. Raw pixels only. (Every "cleanup" I did made it read faker.)
2. **No window-in-a-window.** The window floats directly on the hero background with only a
   shadow. Corollary: never put the capture through the studio's vitrine slot (see traps).
3. **Taskbar = a small band, not the user's real full bar.** "A couple chrome icons and a couple
   edge icons" + the BADGED nb icon (user photo + Adom mark) underlined as active. 68px band,
   54px tiles, left-aligned - pup's exact band spec. NOT Start/Search/pinned-app zoo (rejected),
   NOT a stylized floating pod (rejected), and nothing may be "cut off" below a taskbar.
4. **Family geometry is law** - taken from pup's dev-skills/pup-hero-image (they measured
   Bridge's hero): vitrine y=140 w=1062 h=1050, RIGHT EDGE = PILL RIGHT EDGE = W-52; pill
   Familjen-700 26px pad 22x12 fill (0,206,194) inset 52; logo 236px at (90,64); bg #0d1117 +
   teal glow. kicad + fusion match. Do not eyeball; the constants are in the build script.
5. **Title**: hero headline is the short form "Native Browser" (page stays "NB - Native Browser
   Bridge" - the studio has ONE field for both, #33). Two lines, leading 0.92em ("tighter, one
   message"), as large as fits the column (200px; family 240px is for one-word names). Subtitle
   54px below (pup's offset; 36px was "a tinge close"), accent #4aa8ff on "Your real".
6. **Alignment is measured, not trusted.** Familjen reports NO left side bearing via
   PIL.textbbox, so metric compensation silently no-ops and the title sat 13px off the margin -
   John caught it from a zoomed phone crop after the linter said CLEAN. build_nb_hero.py renders
   each line to scratch, finds the actual first ink column, and offsets so every line's ink
   starts at exactly x=90. Verify after any font/text change: scan ink-left per line on the
   output PNG.

## Traps (the pain, so you skip it)

- **The studio's vitrine slot CANNOT show a browser window honestly**: every vitrine gets a
  `.window` wrapper (border, radius 9, shadow, glow, OPAQUE bg - alpha killed). `--chrome none`
  removes only the fake titlebar. Full-import is the only clean path (#34).
- **Occlusion-blank captures**: a background Chrome stops painting - desktop_screenshot_window
  returns the frame with BLACK content. Detect by max-saturation scan; nudge a paint with
  nbrowser_screenshot (CDP) or recapture while it draws. pup's Chrome ships anti-occlusion
  flags; the user's real Chrome does NOT.
- **Screenshots cap at 1568px wide** -> taskbar icons are ~24px native. Height-normalize each
  icon crop at TRUE aspect (squares smoosh them - John caught that too) and upscale ~2x with
  UnsharpMask. Icon crops in assets/taskbar_strip.png: chrome+profile 499-523, chrome 697-728,
  edge 576-606, badged-nb 899-934 (y 20-63).
- **The studio is a stale snapshot** (#33): its header/PAGE FIELDS title is a pose-time local
  record, NOT the live wiki title. Never click "Apply changes to wiki" - it renames the real
  page to the hero headline. Refresh a stale brief with POST /api/pages.
- **Full-import leaves studio overlays armed** - set overlay.pill=false or you ship two pills.
- **The subtitle renders only the FIRST SENTENCE of brief** - one sentence, commas.
- **--type bridge works** for the BRIDGE pill even though state.enums.mode only lists
  app/component (#27/#34).
- **Screen captures for taskbar material**: crop the strip immediately and DELETE the full
  capture unviewed - John's screen carries sensitive threads. The bar's true top is ~20px into
  a bottom-6.5% crop (row-profile to find it).
- The full hero-studio grievance ledger from this campaign: #28, #33, #34 (+ #25/#26/#27 and
  kicad's #29-#32). Read them before proposing studio-side fixes.
