← Home gallery

CICERO · HOME · MACRO 1

Map · Mapbox-Flutter spec

The first macro — geometry. Built on Mapbox Maps SDK for Flutter (iOS + Android), not web. This page is a design spec: every asset, state, and behavior the Flutter implementation needs. The HTML drawings here are static; the real map is camera-driven, gesture-driven, and runtime-styled.

6 zoom tiers

Each tier surfaces a different cluster shape. Pinch in/out triggers card-type swap. This is one of the biggest behaviors in the system — the home cluster reshapes as the user zooms.

Z 3–5

Continent

Country borders, major cities. One pin per nation.

🇮🇹
🇫🇷
🇪🇸
1 pin per nation→ Nation card
Z 5–8

Nation

Major cities, regions. 1–3 markers per region.

🏛
🏛
🏛
1–3 per region→ City card
Z 9–12

City

Neighborhoods, major districts. Anchor pins only.

Trastevere
Monti
Anchors only→ Neighborhood, Stretch
Z 12–14

District

Many POIs, transit lines visible. 8–15 visible pins.

🏛
🍝
🌿
🍷
8–15 pins→ Place, Stretch, Route
Z 14–16

Neighborhood

Streets visible, all POIs. 15–30 pins, focused state common.

🏛
🍝
🌿
15–30 pins→ Place (focused), Next Up
Z 16–19

Street

Building-level, doorway pins. <8 visible. Standing-in-front cluster.

Santa Maria del Popolo
<8 pins→ Place (Standing in front #11)

Pin categories · 14 icons

Squircle pin chassis (matches the reference screenshot). Each icon is a category. Sprite atlas in production: 14 icons × 6 states × 2 resolutions = ~168 PNG variants.

🏛

Museum

Church / basilica

🗿

Sculpture / monument

🍝

Trattoria

🍷

Bar / aperitivo

🍨

Gelato

Coffee

👁

Viewpoint

🌿

Garden / park

🚇

Transit

🛍

Shop

WC

Toilet

Pharmacy

Generic POI

Pin states · 6

Same chassis, different visual treatment per state. Backend swaps state via runtime style change (icon-image + paint properties).

Default

Beige fill, neutral. Ambient pin.

Focused

Yellow, 1.4× size, bounce on entry.

Pinned (+)

User pressed + when far. Will resurface.

Completed (✓)

Visited. Cyan with checkmark.

Soft-pinned

Pinned for the day. Faded yellow + dashed.

Suggested

Outline only. Next Up candidate.

Stretch lines

Line geography — a street, alley, riverside, garden walk worth doing as a path. Terracotta to distinguish from Routes (slate).

Continuous · default

Default stretch. 4px solid, terracotta, rounded caps.

Dashed · soft-pinned

Soft-pinned for the day. 8/4 dash pattern.

Dotted · suggested

Suggested but not committed. 2/4 dot, 60% alpha.

Route polylines

Treasure-map-not-GPS principle — routes encourage deviation. Slate color (distinct from Stretch terracotta), default = inviting dashed, never directive.

Dashed inviting · default

Default route. 6/4 dash, soft-pinnable. Rounded caps.

Continuous · committed

User is on this route, on track. Solid 4px slate.

Faded ghost · diverged

User wandered off. 30% alpha, dashed.

Neighborhood polygons

Hand-drawn feel. 8–14 vertices, smoothed bezier. Sage green, three states.

Default

Trastevere

Visible at City tier. 12% sage fill, no stroke, name label centered.

Soft-pinned for the day

Monti ✓

22% sage fill, 1.5px dashed sage stroke. Checkmark on label.

You are inside

exploring · Pigneto

18% sage fill, 2px solid sage stroke. "Exploring" eyebrow on label.

Towards X — anchor + path + detours

When brain is in #19 mode. Anchor is large + named. Dashed slate path from user to anchor. Detour pins along the way pulse subtly to invite.

🏛
Colosseum · 12 min
🗿
🌿

Next Up · 3 candidates on the map

When the Next Up Selector card surfaces with 3 options, the map shows all 3 candidates as Suggested pins (outline-dashed) with their respective routes from the user dot, each labeled with time. On tap of a tile → that path becomes solid, the chosen pin transitions to Focused, the other two fade. Camera flies to frame user + chosen.

A · 3 candidates visible (before tap)

All 3 in Suggested state. 3 dashed paths from user dot. Time labels at midpoints.

🍨
🌿
2 min
5 min
8 min

B · After tap (Gelato chosen)

Chosen path becomes solid. Chosen pin → Focused. Others fade to ghost. Camera flies to frame both.

🍨
🌿
5 min →

User location marker

Apple-style. Inner blue dot, pulsing halo, optional direction cone (when device heading available), optional accuracy circle (when GPS poor).

Default

With direction cone

Anchor callouts

For booked anchors (#25) and the day's hero anchor. Larger pin · persistent label · subtle glow when within 200m.

🏛
Vatican · 14:00
Borghese · 90 min slot

Native Mapbox · Flutter implementation notes

Handoff notes for the Flutter build. The HTML drawings above are specifications; here's how each surface maps to Mapbox SDK constructs.

Sprite atlas

  • Generate from Figma component export → cicero-pins.png + cicero-pins.json manifest
  • 14 categories × 6 states × 2 resolutions (1×, 2×) = ~168 PNG variants
  • Bundled with the Flutter app, loaded into the map style at init

GeoJSON sources

  • Separate sources per layer kind: pins · stretches · routes · neighborhoods · user-location
  • Pin features carry { category, state, anchor_id, label } — runtime style picks icon + colors
  • Line features carry { kind, state } — used by line-dasharray + line-color expressions

Layer types

  • Symbol layer for pins — icon-image from sprite, text-field for callouts, icon-allow-overlap: true for focused state, icon-size interpolated by zoom
  • Line layer for stretches/routes — line-dasharray per pattern ([2,1] dashed, [1,2] dotted, no array = solid), line-cap: round
  • Fill layer for neighborhood polygons — fill-opacity per state (12/22/18%), separate line layer for stroke
  • Circle layer for user dot — circle-radius animated in code (Flutter side), not in style spec

Camera + transitions

  • Next Up commit transitionflyTo with custom easing (~600ms, ease-out cubic) framing user + chosen pin
  • Zoom-tier transitions — toggle layer visibility via layer.visibility = "none" / "visible" on zoom-change events
  • Pin focused-state bounce — animate icon-size with tween (Flutter) on selection event

Touch handlers

  • onMapClickqueryRenderedFeatures at tap point with layer filter — find which pin was hit
  • Hit zone larger than visual (touch target ~44×44 minimum) — extend with transparent buffer in sprite
  • Map pan/zoom respects two-finger gestures; one-finger drag pans, pinch zooms

Style spec

  • Single Mapbox style URL (custom Cicero style hosted on Mapbox Studio or self-hosted)
  • Light terracotta/sage palette base, custom roads, custom water (the Tiber as pale blue)
  • 3D terrain off (we want the playful flat-with-3D-buildings feel from the reference screenshot)
  • Building extrusions enabled at street zoom — adds the city-as-postcard feel

Sources of truth (per asset)

  • Pin sprite — Figma cicero-pins component set → exported PNG sprite
  • Polygon coordinates — Mapbox tileset of Rome neighborhoods (custom uploaded)
  • Routes — backend-curated paths (GeoJSON LineString), not Mapbox Directions API turn-by-turn
  • Stretches — backend-curated paths along streets, not auto-routed

Why not web Mapbox? The home is mobile-first iOS/Android. Mapbox GL JS would mean a different rendering pipeline. Sticking with native SDK avoids two implementations.

Why HTML mockups for a native build? Faster iteration on visual design before committing to Flutter widget tree. Once locked here, the spec → Flutter is a one-week port, not a discovery loop.

Source spec for backenddocs/specs/brain-pill-and-map-plan.md · use case table → docs/specs/cicero-home-use-cases-table.md