The source file
The brand arrived as an Illustrator file (kandora_id.ai) and a flattened export (SVG_Asset 1.svg). Inside, there is no structure to lean on — just 18 raw shapes floating in one 987.01 × 505.1 viewBox, painted in exactly two inks: slate #4b5761 for the type, flame #f59221 for the two brushstroke marks. No groups, no names, no per-letter layers. Every glyph is an absolute-coordinate <path> sitting in master space, so position is encoded in the geometry itself, not in any transform.
Extraction in Claude Code
There was no Illustrator round-trip and no manual slicing. A short Python script (_split_letters.py) read the flattened SVG and rebuilt the structure analytically:
1 · Parse & classify. Walk every <path> and <polygon> with ElementTree; bucket by fill class — cls-2 (flame) is the two brush marks, cls-1 (slate) is all the type.
2 · Find the rows. For the slate glyphs, sort by vertical center and split at the largest gap between centers — that gap is the line break, separating BEN KANDORA from DESIGN with no hard-coded threshold.
3 · Order & assert. Sort each row left→right by glyph center, then assert the counts: 10 top, 6 bottom, 2 marks. If the file ever changes, the assert fails loudly instead of emitting garbage.
4 · Crop tight. Compute each glyph's own bbox via svgelements, emit a standalone SVG whose viewBox is that bbox, and carry an origin-normalizing transform="translate(-xmin -ymin)" so each letter sits at 0,0 yet keeps its exact original outline.
# bucket by fill class, then split slate glyphs into two rows orange = sorted([r for r in recs if r["cls"] == "cls-2"], key=lambda r: r["cx"]) gray = [r for r in recs if r["cls"] == "cls-1"] # the line break is simply the biggest vertical gap between glyph centers ys = sorted(r["cy"] for r in gray) gaps = [(ys[i+1] - ys[i], (ys[i+1] + ys[i]) / 2) for i in range(len(ys)-1)] _, thresh = max(gaps) top = sorted([r for r in gray if r["cy"] < thresh], key=lambda r: r["cx"]) bot = sorted([r for r in gray if r["cy"] >= thresh], key=lambda r: r["cx"]) assert len(top) == len(TOP_WORD) # "BENKANDORA" -> 10 assert len(bot) == len(BOT_WORD) # "DESIGN" -> 6 assert len(orange) == 2 # the two BK marks # each glyph cropped to its own bbox, normalized to the origin dx, dy = -rec["xmin"], -rec["ymin"] body = f'<path fill="{fill}" transform="translate({dx} {dy})" d="{geom}"/>'
The result: 18 clean, independent SVG files — each one tight to its own letter, ready to be placed, animated, or recolored on its own.
The eighteen letters
Every glyph below is its actual extracted file, rendered inline at its native geometry — not an image, not a screenshot. Hover any cell.
cls-2 #f59221The case of the missing "I"
One glyph misbehaved. design_04_I is the narrow vertical bar of DESIGN — and unlike every other letter, it exported as a <polygon> whose points already lived in master space, so its origin-normalizing transform came out as a no-op. Dropped naively into the comp, it collapsed to the top-left origin instead of sitting between the S and the G. The fix: read its real slot back out of its neighbors' tracking — ≈ (526, 455) — and translate it there explicitly.
Comped in the Claude webapp
With clean parts in hand, the pieces were reassembled in the Claude webapp into two motion comps — both reconstructed in the original 987 × 505 master space so spacing matches the source exactly. All color routes through --ink / --flame CSS variables, so the brand hues stay locked while the stage can be re-themed.
A · Assembly
The two flame marks fly in first with a focus-pull and slight spin, then BEN KANDORA assembles left-to-right on a spring settle, and DESIGN lands lighter and tighter beneath. Web Animations API, ~3s.
B · Graded fill
The BK mark becomes a clip mask; a jittered grid of Bernoulli-random circles, squares and triangles pops in to fill it, shaded along the diagonal from pale amber → flame → ember. Canvas + a hand-rolled D3-style color ramp.