# UrbanPixel Design System

> Transparent glass LED civic media for smart-city infrastructure.

UrbanPixel is a public-media platform that replaces disposable printed banners with **transparent digital displays** for local governments and small businesses. The brand sits at the intersection of **public infrastructure**, **carbon-neutral city policy**, and **local commerce**. Every visual decision in this system is built around four ideas: *civic*, *transparent*, *clean*, and *premium-but-practical*.

The system is **Korean-friendly** by default. Korean public-sector proposals (시·구청 제안서, BTO 사업 설명서) and Korean-Latin mixed copy are first-class.

---

## Sources

- **Brand brief / repo**: `snap22snap/urbanpixel-design-system` (branch `Codex`) — single document. Provided the brand position, messaging tone, palette direction, and product scope. No code, components, fonts, or imagery were included.
- **Project brief**: contained the visual direction, color direction, typography direction, and example messaging used throughout this system.
- **IR deck (2026-04-11)**: `uploads/UrbanPixel_IR_투자제안서_20260411.pdf` — 30-slide investor proposal. Source of truth for **business model (기부채납)**, **unit economics** (1대 설치비 6,500만원, 20구좌/대, 50만원/구좌, 월 1,000만원), **3-year roadmap** (50 → 100 → 200 screens), **M&A exit** (EBITDA × 6), Smart Bike Station product (따릉이 2,800 거점 활용), and the 5-자치구 S급 rollout (중랑·은평·성동·구로·송파).

## Business model — corrections from IR

The brand brief described UrbanPixel's deal structure as **BTO** (Build–Transfer–Operate). The IR deck clarifies it as **기부채납** (donation/transfer-in-kind to the local government, with **5 + 5 year** operating rights). Both share the same shape — private capital installs, ownership transfers, operator runs ads — but the legal frame and Korean term are different. **Use 기부채납 in all UrbanPixel proposal copy.** Avoid the English term "BTO" unless explicitly translating for an investor audience.

> Everything below this line is an **interpretation** of the above brief. There is no Figma file, codebase, or screenshot reference; UI components and slides are originals built to match the brand description.

---

## Index

| File / folder | What it is |
| --- | --- |
| `README.md` | This file — brand context, content fundamentals, visual foundations, iconography. |
| `SKILL.md` | Skill descriptor for cross-environment use (e.g. Claude Code). |
| `colors_and_type.css` | Foundation tokens — colors, type, spacing, radii, elevation, motion. Import this from any artifact. |
| `assets/logo-urbanpixel.svg` | Wordmark, primary on light backgrounds. |
| `assets/logo-mark.svg` | 64×64 pixel-grid mark only. |
| `assets/logo-urbanpixel-onnight.svg` | White wordmark for navy / imagery / night surfaces. |
| `assets/scene-smart-placard.svg` | Hero scene — daylight, transparent placard with sample content. |
| `assets/scene-night-emergency.svg` | Reserved night/emergency-broadcast hero scene. |
| `preview/` | 17 design-system cards (Colors, Type, Spacing, Components, Brand) shown in the Design System tab. |
| `ui_kits/civic-web/` | Public-facing proposal/marketing site UI kit. `index.html`, `components.jsx`, `styles.css`, `README.md`. |
| `ui_kits/cms/` | Civic CMS console UI kit (campaigns, screens, live preview). `index.html`, `components.jsx`, `styles.css`, `README.md`. |
| `slides/index.html` | 7-slide proposal deck (title, section, stats, comparison, BTO diagram, quote, closing). |
| `slides/deck-stage.js` | Slide-deck shell — handles scaling, keyboard nav, print-to-PDF. |

---

## Content Fundamentals

UrbanPixel copy is **proposal-ready**: short, declarative, civic. It must read well to a 도시계획과 공무원 (city planning officer), an investor, and a 동네 베이커리 사장님 in the same paragraph.

**Voice**
- **Confident, civic, calm.** Never breathless. Never startup-y.
- **Public value first, advertising revenue second.** Every commercial line is balanced by a public-good line.
- **Korean-friendly.** Headlines work in either language; mixed-script (한글 + Latin) is normal. Avoid English idioms that don't translate cleanly.
- **No emoji.** No exclamation points. Periods only. (Korean copy may end without punctuation when used as a label.)

**Casing**
- Headlines: sentence case in English ("From disposable banners to sustainable civic media."). Korean headlines as written.
- Eyebrows / labels: ALL CAPS in English, with `letter-spacing: 0.14em` (`.up-eyebrow` token). Used sparingly — section dividers and chip categories only.
- Buttons & UI labels: sentence case ("View proposal", "제안서 보기").

**Pronouns**
- Default to **"the city"** / **"your city"** / **"local government"** in English. Avoid "we" except when describing UrbanPixel as the operator.
- In Korean, use **"지자체"**, **"도시"**, **"동네"** rather than first/second person.

**Punctuation & numbers**
- Use periods at the end of full sentences. Drop them on chips, eyebrows, captions.
- Numbers: thousands separators always (`12,400 displays`, `12,400대`). Use the mono token `.up-mono` for stat readouts and CMS data.
- Never use scare quotes or em-dash dramatics.

**Vibe**
- Reads like a **public-sector proposal cover**, not a SaaS landing page.
- Every claim has an implicit "for the city" attached.
- No hype words: avoid "revolutionary", "game-changing", "next-gen", "스마트한 미래" cliches.

**Approved phrases (use directly)**
- From disposable banners to sustainable civic media.
- Visible when needed, transparent when not.
- Public information, local commerce, and city scenery in one transparent screen.
- A smarter alternative to printed banners.
- Local advertising, managed with public order.
- Carbon-neutral media infrastructure for the city.
- Public value first, advertising revenue second.
- The city communicates without blocking the city.

**Phrases to avoid**
- "Revolutionize urban communication" — too marketing.
- "Next-generation smart city solution" — empty.
- "🚀 Launch your local ad campaign!" — wrong register entirely.
- "Cyberpunk", "futuristic", "dystopian neon" — opposite of brand.

---

## Visual Foundations

### Color

The brand operates on a **3-color triad** locked in by the user (see `chats/chat1.md`). The token names `--up-glass-*` and `--up-cyan-*` are kept for backwards-compatibility with existing components — values were repointed, not renamed.

- **Base**: `--up-ivory` `#FBF9F4` for full pages, `--up-clear` `#FFFFFF` for elevated cards. `--up-mist` `#F2F5F8` for cool section bands.
- **Brand primary** — Electric Violet `--brand-primary` / `--up-glass-500` `#9754FF`. Used for primary CTAs, links, focus rings, brand emphasis. Hover `#7A2EE6`, press `#5A1AB8`.
- **Secondary · tech / transparency** — Civic Blue `--accent-tech` / `--up-cyan-500` `#356CFE`. Used on glass surfaces, CMS status, "live" indicators, local-commerce chips.
- **Secondary · public value** — Civic Green `--accent-public` / `--up-green-500` `#2FB978`. Carbon-neutral chips, public-content tags, event highlights.
- **Ink**: Charcoal `#1F2A33` for body, Deep Navy `#0E2545` for headlines and dark surfaces.
- **Emergency**: A single dedicated red `--accent-emergency` `#EC4455` reserved exclusively for emergency-broadcast UI. Never use it as a brand color.

> **Legacy accents (do not use as new brand colors)**: Soft Coral `--up-coral-500` `#EC7A5A` and Warm Yellow `--up-yellow-500` `#E5B834` are retained as tokens for one-off legacy surfaces but were retired from the brand triad in the chat1 finalization. New work should compose from the violet/blue/green triad only.

The page should feel like **morning light through glass**. If a layout has more than ~10% saturated color across its surface, it has too much.

### Typography
- **Family**: **SUIT** — Korean-friendly geometric humanist sans, dual-script metrics. Self-hosted from `fonts/SUIT-*.ttf` (9 weights: Thin 100 → Heavy 900). The CSS family name is `"Suit"`.
- **Mono**: IBM Plex Mono for CMS data, telemetry, timestamps. Loaded from Google Fonts CDN.
- **Scale**: display 88 / h1 56 / h2 40 / h3 22 / h4 18 / body 16 / sm 14 / caption 13 / eyebrow 12.
- **Weights used**: 400 (body), 500 (chips/labels), 600 (UI/h3-h4), 700 (display/h1/h2). 100 / 200 / 300 / 800 / 900 are loaded but reserved.
- **Tracking**: tight on display (`-0.02em`), wide on eyebrows (`0.14em`). Body untracked.
- **`text-wrap: pretty`** on all body copy. **`text-wrap: balance`** on headlines.
- **Korean-friendly wrapping**: `word-break: keep-all` is set globally to prevent mid-syllable breaks. Keep this on all custom components.
- **CDN fallback** (if you can't self-host): `https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_suit@1.0/SUIT-{Weight}.woff2` — 9 weights from Thin to Heavy.

### Backgrounds
- Default to **flat ivory or clear**.
- Section bands: a single color shift (`--up-mist`) between bands — never a gradient between sections.
- Hero scenes: a soft top-to-bottom **sky gradient** (ivory → mist) with a **transparent placard SVG** centered. Day-scene and night-scene assets live in `assets/`.
- **No** patterned or repeating textures. **No** hand-drawn illustrations.
- Imagery, when used, is **cool-toned, daylight, real urban photography** (not provided in this system; bring your own). Avoid grain, sunset oranges, and stylized filters.

### Glass & transparency
- The brand's signature surface. Use `.up-glass` (rgba white + `backdrop-filter: blur(18px)`).
- Apply **only over imagery or color** — glass on plain ivory is invisible. If there's nothing behind it, use `.up-card` instead.
- Glass borders are `rgba(255,255,255,0.55)` — soft, never bright.
- **Don't pile glass on glass.** One glass layer per region, max.

### Borders & dividers
- `--border-hairline` (`rgba(15,40,70,0.08)`) is the default 1px line.
- `--border-thin` (`0.14`) for inputs and emphasized cards.
- `--border-strong` (`0.24`) for hover states and active selections.
- Dividers are 1px hairlines. **Never** use a 2px or 4px section divider.

### Elevation
- Four-step shadow scale (`--shadow-1` → `--shadow-4`). All shadows are tinted with **navy**, never pure black, so they read cool and quiet.
- **`--shadow-glass`** is reserved for the glass surface — has an inner top highlight that simulates a lit edge.
- Elevation is calm: hover should feel like the card "wakes up" 1 step, not "lifts off".

### Corners
- `--r-md` (10px) is the workhorse for buttons, inputs, cards. `--r-lg` (14px) for surface cards. `--r-2xl` (28px) only for hero panels. `--r-pill` for chips.
- **Never** mix radii within a single component cluster.

### Motion
- **Easing**: `--ease-civic` (`cubic-bezier(0.22, 0.61, 0.36, 1)`) is the default — calm decel.
- **Duration**: 220ms is the default. 140ms for cosmetic, 420ms for spatial transitions.
- **No bounces, no springs, no sweeping translate-Y entrances.** This is civic infrastructure, not a fitness app.
- Page-level motion: fade + 4-8px translate. Nothing larger.

### Hover & press
- Buttons: hover = +1 step on background fill (`primary` → `primary-hover`). Press = `--brand-press` shade + 1px translateY down. No scale.
- Cards: hover = `--shadow-3` (one step up). No scale. No glow.
- Links: underline on hover, color stays.
- Disabled: 40% opacity, no other change.

### Layout
- Container: `1200px` (`--container`), `1320px` for proposal layouts.
- Gutter: 24px. 8-column grid for product pages, 12-column for proposal/marketing.
- **Modular cards** are the default unit of layout — every section is a row of cards or a single hero card.
- Comparison tables get equal-width columns and a hairline grid; the "UrbanPixel" column is **subtly** highlighted (`--up-glass-50` background), never with a colored border or banner.

### Diagrams
- Stroke-based, 1.5px line weight, navy ink. Filled regions use 50-tier brand colors at 60% opacity. Arrows are open (`>`), not solid heads. BTO and CMS flows always read **left → right**.

### Density
- Generous. Section vertical padding is `--s-16` (64px) on desktop, never less than `--s-10` (40px). Card internal padding is `--s-6` (24px) minimum.

---

## Iconography

UrbanPixel uses **[Lucide](https://lucide.dev)** as its icon set, loaded from CDN (`unpkg.com/lucide-static@latest`). This is a **substitution** — the brief did not ship an icon set. Lucide was chosen for its civic, thin-stroke (1.5px / 2px), geometric character that matches the brand's "calm but visible" line work.

**Rules**
- Stroke `1.75px`, never filled. Color: `currentColor`, defaults to `--fg-secondary`.
- Sizes: 16, 20, 24. Inline icons are 16; UI buttons are 20; section icons are 24.
- **No emoji anywhere** — public-sector copy must read formally.
- **No unicode symbols as icons** (✓, ★, →). Use the Lucide equivalent (`check`, `star`, `arrow-right`).
- The pixel-mark logo (`assets/logo-mark.svg`) is the **only** UrbanPixel-original glyph. Don't draw new ones.

**Common icons used in this system**
- `radio-tower` — broadcast / public messaging
- `leaf` — carbon-neutral
- `store` — local commerce
- `triangle-alert` — emergency
- `bike` — smart bicycle station
- `monitor` — CMS / displays
- `building-2` — civic / municipal
- `calendar-days` — events
- `eye` / `eye-off` — visible / transparent (the brand's central duality)

If you need an icon that isn't in Lucide, do **not** draw an SVG by hand. Use the closest Lucide match and flag the substitution.

---

## Substitutions to flag

1. **SUIT — self-hosted.** The brand sans is **SUIT** (Korean-friendly geometric humanist), self-hosted from `fonts/SUIT-*.ttf` and registered as the CSS family `"Suit"`. A jsdelivr CDN fallback exists at `cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_suit@1.0/`. If the operator licenses a different family (SCDream, Spoqa Han Sans Neo, a custom face), drop the files into `fonts/` and update `--font-sans` in `colors_and_type.css`.
2. **Lucide icons.** No icon set was provided in the original brief. **Action**: If UrbanPixel commissions a custom icon family, replace the CDN link and drop SVGs into `assets/icons/`.
3. **Imagery.** No real urban photography was provided. The hero scenes in `assets/` are SVG illustrations. **Action**: Replace with cool-toned, daylight, real urban photography for production use.

---

*Last updated: 2026-04-28*
