← Back to Component Index

ALPA Mobile — Component Design Questions

A living tracker of open design-team-facing questions surfaced while mapping the Figma components to the mobile component library. Each item is something we need a design decision or confirmation on before the spec can be called final — distinct from backend data gaps, which live in the Backend API Mapping Report.

Base design: owEYzHf7FrHRvWC2u82UOl · MEC file: gpO3masyyNNHxjvRtdcRo7 Updated: 2026-08-28 11:47 ET Work Item: AB#2095 Status: Living — 16 open · 2 tentative

Figma file reference

There are two Figma files for this project. Questions below that link to Figma nodes use one of these two files:

Label used in questionsFile IDWhat it isOpen in Figma
Base design owEYzHf7FrHRvWC2u82UOl Original ALPA Mobile app design — base components and screens, no MEC customizations Open base design
MEC customization file gpO3masyyNNHxjvRtdcRo7 Copy of the base file extended by Interactive Strategies — contains the per-airline MEC screens (UAL, FDX, DAL) and design annotations Open MEC file
16
Open
2
Tentative
13
Resolved
2
Withdrawn

How to use: design reviews each open question, records the decision in the Design answer row, and we fold it back into the affected component spec(s). Resolved items move to the Decided section with a date.

Open Questions

IDQuestionComponent(s)TypeStatus
DQ-1 Body font: token is Futura PT, but some layers override to Futura LT Pro Button, Button Group, marketing Hero Figma-file inconsistency Resolved 2026-06-22
DQ-2 card-lg image accent — blue only (static); yellow variant may be removed CardHero / card-lg Behavior / data binding Resolved 2026-06-26
DQ-4 Futura PT weight gap — Medium, Demi, Heavy expected to be provided; full weight set planned for bundling Button Group, navBar-bottom, Flight Card, tabs Font asset availability Tentative
DQ-5 Component sizing deep-dive — phone-scope heights confirmed; navBar-bottom-tablet height and tablet variants still deferred ButtonCard, FlightCard, navBar-bottom-tablet, DutyPeriodCard Sizing / dimensions Resolved 2026-06-22
DQ-6 topNav-og — confirmed identical to topNav (393×98 px); topNav-og is authoritative per D15 pattern topNav / navBar-top Figma inventory confirmation Resolved 2026-06-22
DQ-7 United Airlines MEC palette — all 5 hex values confirmed (Blue-800/600/500/400, Grey-100) MEC theming tokens Token values / Figma API Resolved 2026-06-22
DQ-8 Heading font role normalized to Futura PT — Lora replaced (Eyebrow/Inter unchanged, out of scope) Heading text layers (base + MEC, pending MEC font delivery) Font normalization Resolved 2026-07-23
DQ-9 FlightCard scaffold ViewModel — base CardViewModel sufficient or needs dedicated FlightSegmentCardViewModel? Flight Card domain control Architecture decision Resolved 2026-06-22
DQ-10 flight detail flight info — new scaffold component or extend FlightSegmentCardViewModel? FTDT detail panel · Flight card expanded view Architecture decision Resolved 2026-06-22
DQ-11 Compact flight segment rows inside card-duty period-alt — embedded in DutyPeriodCardViewModel or separate FlightSegmentRowViewModel? Duty Period Card (FTDT) Architecture decision Resolved 2026-06-22
DQ-12 Spacing tokens — canonical 14-step Figma spacing scale located; the 3 off-grid outliers resolved by the adopted consistency rule (D-S1) All containers (Carousel, Grid, Stack, ButtonGroup), Flight Card, Notification Card Missing Figma annotation Resolved 2026-06-30
DQ-13 Loading / skeleton pattern for dynamic feed — client passes since date for differential updates; skeleton UX for initial load vs background refresh undecided Home feed, all container types UX pattern / Figma annotation Tentative API approach
DQ-14 MEC airline fonts — which weights needed; separate license files being procured? (weight policy settled 2026-07-29: base = Regular/Book only, MEC customisation is the exception) UAL, DAL, FDX MEC themes Font asset / licensing Open — procurement
DQ-15 DAL two-tone brand navy — intentional primary/secondary surface pattern or Figma inconsistency? DAL MEC theme tokens Token design decision Open
DQ-16 Placeholder / fallback image assets — what displays when a remote image URL is missing or fails? Hero Card, Text Card, Small Card, Button Card, Pilot avatar Missing asset / UX pattern Open
DQ-17 Icon strategy — dual: interim custom Figma vectors are in use (BottomNav wired, PR #1664); an icon-font capability for the WebView is still a forward requirement (licensing + wiring open). Page Template (bottom nav) · every screen Icon asset / font availability Open
DQ-18 Bottom-nav selected/active tab indicator — Figma has no annotation for the selected state. Scaffold improvised blue label + blue icon. What is the canonical treatment? Page Template (bottom nav) · BottomNav component Missing Figma annotation / state spec Open
DQ-19 Render-surface + host strategy — XAML-first Shell vs Blazor-first host; which surfaces ship native XAML vs Blazor; how to navigate between them. Chrome abstraction keeps it low-regret (mixed → full-native fallback). App host / Shell · all surfaces · component-library render abstraction Architecture / platform Open
DQ-20 Saved Search/Flights action affordance — swipe-to-delete (Figma) vs visible action column (scaffold). First concrete instance of DQ-19. Saved Search Card · Saved Flights (Flight Segment Card) Interaction / platform-fit Open
DQ-21 Type scale & accessibility text scaling — Dynamic Type / OS font-scale across Blazor (WebView, no auto-scale) and native XAML (auto). Relative-type tokens + WebView scale-bridge + reflow. Design tokens (typography) · all text-bearing components · Blazor host Accessibility / design-system + platform Open
DQ-22 Content-item ID scheme — withdrawn: assumed a composite key (retracted premise). The real contract has no composite key — MenuItem/DocumentItem via MobileContent. Content Contract Backend / API contract Withdrawn
DQ-23 Content resolve endpoint — withdrawn: no /api/content/resolve; content is fetched by Scope/Category (documents) or the menu (navigation) via MobileContent. Content Contract Backend / API contract Withdrawn
DQ-24 Content-type ↔ component compatibility — resolved by the Content Contract §4 mapping (MenuItem-backed navigation vs DocumentItem-backed content). Content Contract · dynamic-feed components Spec / product Resolved
DQ-25 League Spartan font override on base-app (non-MEC) text — Expander trigger label, "Save Search" button, and search secondary-button label Flight Finder Advanced Search (Expander), Search Secondary Button Figma-file inconsistency Open
DQ-26 PromoBanner (D43): is structured banner content a design requirement, or is the current single-image PageBanner adequate? Backend Change 5 on hold pending the answer. PromoBanner / PageBanner Design intent / backend contract Open
DQ-27 Document lists (card-notification): should read/unread state render, and how? Suggestion on the table: unread signalled by a red accent line instead of the dot. card-notification / Internal Comms & ALP Mag document lists Design intent / legacy parity Open
DQ-28 iOS safe areas: the Figma frames don't account for the Dynamic Island / status bar (top) or the home indicator (bottom). Now measured end-to-end on navBar-bottom (AB#2536) — three candidate treatments, screenshots and a recommendation are in the block. All screen compositions / page shells Design gap — platform chrome Open
DQ-29 Flight status chip colors: card master shows all chips amber; Status/* token set defines per-status colors. Resolved 2026-07-29 — mapping confirmed, Early → green signed off; master repaint still outstanding. Flight Card status chips Master vs token discrepancy Resolved
DQ-30 Bottom-nav icon assets — PDR has no icon of its own (reuses the FTDT glyph), and the MEC slot ships as a fixed-tint raster that cannot follow a per-MEC theme. Themeable vectors requested. Bottom tab bar (navBar-bottom) Missing / non-themeable assets Open
DQ-31 KCM Policy Search — its SEARCH POLICIES button is grey-on-grey and reads as disabled, while every other primary action in the app is the blue endcap button KCM Policy Search, Button (primary) Polish / consistency Resolved 2026-08-13
DQ-32 KCM Policy Search — Airline is a select, Airport is free text; Flight Finder's airport fields have typeahead. Is the asymmetry intended? KCM Policy Search, Form field Polish / behavior Resolved 2026-08-13
DQ-33 Airline Policies — a long searchable list with no result count and no no-match state, so a filter that matches nothing looks the same as one that failed Airline Policies (jumpseat directory) Polish / feedback Open
DQ-34 The tab bar with fewer than five slots is not specified. Signed out it carries three; we ship them centre-clustered at the node's slot size. On a tablet the bar now clusters at any slot count. Confirmation wanted. navBar-bottom / signed-out shell Design gap — unspecified variant Open
DQ-35 Terminology: the AB#2563 tablet work is "Adaptive Layout" (locked component sizes, discrete layout variants at breakpoints), not "responsive layout" or "smart promotion." Tablet breakpoint system (AB#2563 / AB#2409) Terminology / naming Resolved 2026-08-23
DQ-36 WebView Page (now AuthenticatedWebViewPage) immersive-landscape scope — resolved by AB#2591: ships phone-only, tablets keep their chrome, matching the standing rule AuthenticatedWebViewPage (native, AutoLoginWebViewContentView) Design gap — unspecified scope Resolved 2026-08-27

DQ-1 — Body font: Futura PT token vs Futura LT Pro layer override

Resolved 2026-06-22

What we found (from the Figma source):

  • The design-system variable Font Family/Body resolves to "Futura PT" (alongside Font Family/Heading = Lora, Font Family/Eyebrow = Inter).
  • The canonical Home screen (node 4099:5230) renders only Futura PT — its generated code uses Futura PT Book / Demi and no "LT Pro".
  • However, a handful of standalone component frames' text layers — the Button and Button Group labels and the marketing Hero illustration — carry a direct font override to "Futura LT Pro" that bypasses the Font Family/Body token.

Why it matters: Futura PT and Futura LT Pro are different physical typefaces. If the overrides are unintentional, those buttons/labels would render in a different font from the rest of the app. Our component library standardizes on the Futura PT token, so today we treat the LT Pro instances as overrides to reconcile.

Ask for design

Is Futura LT Pro intentional on those button / hero layers, or should they be re-bound to the Font Family/Body (Futura PT) token? If it's a deliberate display face for buttons, we'll add it to the token set explicitly; otherwise please clear the override in Figma so the layers inherit Futura PT.

Design answer (2026-06-22): Futura PT is confirmed as the single body font family for the app. The Futura LT Pro overrides on Button, Button Group, and Hero layers are not intentional — those layers should be re-bound to the Font Family/Body (Futura PT) token in Figma. All component specs and token tables updated to reflect Futura PT throughout.

Font files confirmed available (2026-06-22): Futura PT Book and Futura PT Bold (.otf). Weights referenced in Figma layer tree but not yet confirmed as available files: Medium, Demi, Heavy — to be resolved at the ALPAMobile.Presentation font-bundling pass (deferred; the library shipped as a Blazor RCL 2026-07-15, D61).

Affected specs updated: button, button-group, card-hero, design-tokens.

DQ-2 — card-lg image accent: blue only (static); yellow variant deferred / may be removed

Resolved 2026-06-26

What we found: the card-lg (CardHero / Document Hero) image carries a 6 pt left-border accent. The default is Border/Blue-lt #007BC2; some instances show a Border/Yellow #F7BB0D variant. The Figma source has no annotation explaining when each applies.

Figma reference: base designcard-lg master (node 4033:1436) — both the blue and yellow accent variants are in the component variant set. The home screen (node 4099:5230) shows card-lg instances with each color in context.

Ask for design

(1) What drives the yellow variant — is it tied to a content attribute (e.g. category / priority / status on the document, which would make it data-driven and require a backing field), or is it static per placement? (2) Is the accent limited to these two colors, or is there a fuller set we should tokenize?

Why it matters: the answer decides whether the backend needs an accent/category field on DocumentItem. Tracked as the deferred design item in the Backend API Mapping Report (Part 1 §4, Open Questions).

Design answer (2026-06-26): Blue only, static. The 6 pt left-border accent on card-lg is always Border/Blue-lt #007BC2 — it is not data-driven and does not vary by content category or status. The yellow variant (#F7BB0D) has no confirmed usage and may be removed from the Figma file entirely. Backend impact: none — no accent field is needed on DocumentItem; the color is hardcoded to the token value at render time.

Affected specs: card-hero, home-screen-data-gap, backend-api-mapping-report (Part 1 §4 — accent field question closed).

DQ-4 — Futura PT weight gap: Medium, Demi, Heavy referenced in Figma but only Book & Bold confirmed available

Tentative answer

What we found: DQ-1 confirmed Futura PT as the single body font family with Book and Bold .otf files in hand. A full Figma MCP sweep of all master component nodes (2026-06-26) found three additional weights in use with no confirmed font file for any of them. Node list below, grouped by weight — for procurement reference.

Futura PT Heavy

ComponentFigma NodeLayer / TextSizeColor
Flight Segment Card 4555:7483 Date/time header — text node 4555:7208 18px #05273e (navy)
Flight Segment Card 4555:7483 Airport codes (DEN, MIA) — I4555:6999;4322:5670, I4555:7004;4322:5670 18px #05273e
Section Title 4596:11818 "Flight Segments" — 4589:9728 18px #05273e
Home Screen (main-feed) 4099:5230 Survey banner title "Survey Now Open" — I4099:5230;4038:893;20652:13451;5512:12934 18px (Font Size/Body/M) #ffffff

Futura PT Demi

ComponentFigma NodeLayer / TextSizeColor
Home Screen (main-feed) 4099:5230 Emergency Hotline button label — I4099:5230;4038:893;4270:5843;5348:29578 15px, uppercase, ls 0.6px #ffffff
Segmented Control (tab group) 4361:8479 Pill labels "Nonstop / One Stop / Multileg" — I4361:8464;4245:5756, I4361:8465;4245:5756, I4361:8466;4245:5708 15px, uppercase, ls 0.6px #05273e / #ffffff
List (listy card generic) 5172:15482 Title "Lorem" — 5172:15472 20px (Font Size/Body/L) #05273e

Futura PT Medium (also referenced as Futura:Medium via the Body font token — same physical file required)

ComponentFigma NodeLayer / TextSizeColor
Bottom Navigation 4038:1037 Tab labels (Home, Jumpseat, KCM, FTDT, My MEC) — I5035:16371;4020:3420 ×5 12px, uppercase #ffffff
Flight Segment Card 4555:7483 "Flight Time" label — 4555:7007 16px #05273e
Home Screen (main-feed) 4099:5230 Survey banner subtitle "Closes June 10 at 11:59PM PT" — I4099:5230;4038:893;20652:13451;5512:12935 12px (Font Size/Body/S) #ffffff
Home Screen (main-feed) 4099:5230 Welcome "View Contract" link — I4099:5230;4038:893;4185:10450 18px, underline #007bc2
Home Screen (main-feed) 4099:5230 Feed header "View My MEC" link — I4099:5230;4038:893;4099:5247;4143:8273 18px, underline #007bc2
Home Screen (main-feed) 4099:5230 Section "View All" link — I4099:5230;4038:893;20652:13468 18px, underline #007bc2
Home Screen (main-feed) 4099:5230 Button card mini-labels (PDR, FTDT, SOAR) — I4099:5230;…;4735:13034 ×3 14px #05273e
Home Screen (main-feed) 4099:5230 Card-lg description — I4099:5230;4038:893;4116:6869;4033:1339 12px (Font Size/Body/M) #05273e
Card Large (card-lg) 4033:1436 Description body — 4033:1339 18px (Font Size/Body/M) #05273e
Card Medium (card-md) 4718:11125 Description body — 4718:11132 18px (Font Size/Body/M) #05273e
Button Card (card-btn) 4737:13038 Label text "United Airlines" — 4735:13034 18px (Font Size/Body/M) #05273e
List (listy card generic) 5172:15482 Subtitle "Fri April 24" — 5172:15473 18px (Font Size/Body/M) #05273e
Notification Card (card-notification) 4864:31901 Body text — 4864:31878 20px (Font Size/Body/L) #05273e
Carousel Scroll Indicator (Slider) 4317:5239 Range labels & value display — 4317:5220, 4317:5231, 4317:5237, 4317:5238 16px #05273e

Node list generated 2026-06-26 via Figma MCP sweep of all master component nodes. 6 node IDs returned "invalid" errors (moved/removed from file): 5452:679, 4864:32242, 5515:55, 4038:221, 5452:434, 5515:52 — excluded above. Run sweep again after any Figma revision to keep current.

Why it matters: Missing weight files at the ALPAMobile.Presentation font-bundling pass mean those text layers will silently fall back to a system font or the nearest available weight, breaking visual fidelity. This needs to be resolved before the font-bundling pass (post #2087).

Tentative answer (2026-06-22): Sticking with Futura PT as the single body font family across all weights. Futura PT Medium, Demi, and Heavy .otf files are expected to be provided alongside Book and Bold before the ALPAMobile.Presentation font-bundling pass. No fallback substitutions planned — the full weight set will be bundled. Confirm file delivery before implementation begins.

Correction (2026-07-23, José, via AB#2358): The above is superseded — only two weights are being sourced: Futura (Regular) and Futura Book. Medium, Demi, Heavy, and Bold are confirmed not coming. As of this correction, zero Futura/Futura PT font files of any weight exist in the repository (this doc's "Book and Bold .otf files in hand" claim above could not be verified against the codebase — worth confirming with whoever holds them, since they aren't committed anywhere the app can bundle). Figma nodes above needing Medium/Demi/Heavy/Bold will need an explicit fallback decision (nearest-available weight, or CSS-synthesized bold) rather than the "full weight set, no substitutions" plan this answer assumed. See DQ-8 for the heading-font resolution built on this corrected scope.

Affected specs: design-tokens (Typography section), Flight Card, button-group. Deferred to the ALPAMobile.Presentation font-bundling pass.

DQ-5 — Component sizing deep-dive: multiple component heights TBD

Resolved 2026-06-22

What we found: The 2026-06-17 Figma revision introduced or updated several components whose exact height values were not extracted during the sync pass. Widths are confirmed; heights were marked TBD pending a dedicated sizing deep-dive.

ComponentWidthHeight (resolved)Source
card-button Default353 px content / 393 px viewport189 px ✓Live Figma node 4074:4624
card-button Variant2353 px129 px ✓Live Figma node 4104:5452
flight card-og Default / Swipe353 px164 px ✓Live Figma node 4713:19868
flight card-og Expanded353 px1361 px ✓Live Figma node 4713:19985
flight card-recent searches353 px115 px ✓Live Figma node 4450:11208
card-duty period Default345 px238 px ✓Live Figma node 4596:11976
card-duty period Variant2345 px1010 px ✓Live Figma node 4601:12136
navBar-bottom-tablet800 px ✓TBD (not in live Figma metadata).fig binary 5452:267; tablet deferred (D12)
flight card-TABLETTBDTBDDeferred (D12)

Why it matters: Heights are required before scaffold ViewModel layout constraints can be written and before MAUI HeightRequest decisions can be made for the native-XAML surface of ALPAMobile.Presentation.

Answer: Sizing pass completed 2026-06-22 via get_metadata on live Figma file. All phone-scope components confirmed. Tablet components remain deferred (D12). navBar-bottom-tablet height is the only remaining TBD — it was not discoverable in the live Figma page metadata; tablet scope is deferred anyway. Component Dimensions table in design-tokens.html and button-card spec page updated with confirmed values.

DQ-6 — topNav-og: confirm equals old topNav (393×98 px) so retired entry can be removed

Resolved 2026-06-22

What we found: The 2026-06-17 Figma revision introduced topNav-og alongside an existing topNav entry in figma-component-specs.json. Both entries share the same dimensions (393×98 px), matching the preliminary analysis.

Confirmation (2026-06-22): get_metadata run on both nodes via the live Figma API:

EntryNode ID (.fig)WidthHeight
topNav4864:32242393 px98 px
topNav-og4153:3892393 px98 px

Both are 393×98 px. topNav-og is the canonical master (the -og pattern follows D15). The plain topNav entry is a stale reference. The figma-component-specs.json inventory retains both entries for traceability; active work references topNav-og. No rename decision required — this follows the already-established D15 pattern.

Answer: Confirmed identical at 393×98 px. topNav-og is authoritative. No further action required; topNav entry in the JSON is a stale alias (same resolution pattern as D15 for flight segment card).

DQ-7 — United Airlines MEC palette: token paths documented but hex values TBD

Resolved 2026-06-22

What we found: The MEC theming token architecture is fully documented in design-tokens.html — the theme token set is the per-MEC customization layer, and the United Airlines palette is the Figma example. The token paths were confirmed; hex values were blank pending a variable read.

Answer (2026-06-22): All five United Brand palette hex values confirmed via get_variable_defs on the MEC frame node 4684:7914 in the Figma app page:

Token PathHexSwatchRole
United Brand/Blue-800#002243Nav bar background — replaces Surface/Brand
United Brand/Blue-600#001262Active states, hover
United Brand/Blue-500#0008ceButtons, borders — replaces Border/Brand
United Brand/Blue-400#94ebfeLinks, CTA accent — replaces Accents/Blue
United Brand/Grey-100#f7f7f7Surface backgrounds, dividers

The MEC palette table in design-tokens.html has been updated with confirmed hex values and inline swatches. The same call also confirmed font families for DQ-8: heading = Lora (SemiBold 600), eyebrow = Inter.

DQ-8 — Heading font normalization: Lora replaced by Futura PT (Eyebrow/Inter descoped)

Resolved 2026-07-23

What we found: DQ-1 confirmed Futura PT as the body font. The DQ-7 variable read (2026-06-22) confirmed the Figma tokens bound Lora SemiBold (heading) and Inter Bold (eyebrow) as separate families.

TokenRoleFigma familyResolution
Font Family/HeadingCard headers, hero titles, pilot card, MEC card titleLora SemiBold (600)Normalized to Futura PT
Font Family/EyebrowEyebrow labels, section headers, date stampsInter Bold (700)Unchanged — out of scope for this decision

Resolved 2026-07-23 (José): Heading normalizes to Futura PT — Lora is no longer the base heading font. Eyebrow (Inter) was not part of this confirmation and stays as-is pending a separate decision. Only two Futura weights are being sourced: Futura (Regular) and Futura Book — see the corrected scope note on DQ-4, which supersedes that question's "full weight range" tentative answer.

Implementation (AB#2358): --font-heading-family in alpa-components.css updated from the Lora stack to the Futura PT / Futura fallback chain (matching --font-body-family). Separately, --alpa-heading / --alpa-body were found to duplicate literal font stacks instead of referencing var(--font-heading-family) / var(--font-body-family) — meaning a MEC theme's Font/Heading / Font/Body override (injected via BlazorThemeServicealpa-theme.js#mec-theme) never reached any component. Fixed to bridge through the semantic tokens, matching the existing --alpa-navy: var(--surface-brand) color-alias pattern. No physical Futura PT font files exist in the repo yet (base or MEC) — rendering currently relies on iOS's built-in system "Futura" via the CSS fallback chain until the licensed files are sourced and registered via @font-face (Blazor) / AddFont() (MAUI XAML).

MEC scope: Documented per-MEC brand fonts (UAL = LeagueSpartan, DAL heading = BebasNeue / body = LeagueSpartan, FDX = TBD per DQ-14) remain the design target and are unchanged by this decision — those come from decision D33. None of those files exist either, so every MEC theme (e.g. theme.UAL.json) renders the interim Futura PT value until its real brand font is delivered.

Verified live (2026-07-23): iOS simulator, /home-preview against live production data. Confirmed via getComputedStyle before and after, not just visual inspection — --font-heading-family moved from "Lora", Georgia, "Times New Roman", serif to "Futura PT", "Futura", -apple-system, "Segoe UI", Roboto, sans-serif, and --alpa-heading (what components actually consume) now resolves the same way, proving the token bridge works end to end.

Home Preview headings in Lora before the fix
Before — Lora (serif)
Home Preview headings in Futura after the fix
After — Futura (sans)

DQ-9 — FlightCard scaffold ViewModel: base CardViewModel sufficient or needs FlightSegmentCardViewModel?

Resolved 2026-06-22

Decision: Option B — FlightSegmentCardViewModel : CardViewModel (recorded as D16 in the Decisions Record).

Field inventory vs CardViewModel:

Required fieldIn CardViewModel?Option A mapping
Origin (IATA code)NoConcatenate into Header or ContentText
Destination (IATA code)NoSame blob
DepartureTimeNoSame blob
ArrivalTimeNoSame blob
DurationNoSame blob
FlightNumberNoSame blob
AircraftTypeNoSame blob
StatusBadge (text + color)NoCannot represent color in a plain string

Why Option A fails the threshold: 8 fields must be independently rendered in discrete visual slots (origin/destination city codes, separate time row, leg detail strip, status badge). No fewer than 6–7 fields would have to be concatenated into ContentText — well above the 3–4 concatenation threshold. StatusBadge additionally requires a color value that has no representation in a string property. Type safety in the factory and clarity in the XAML bindings both require typed properties.

Answer: Option B adopted as D16. FlightSegmentCardViewModel : CardViewModel was initially the 10th scaffold component. Amended by D17: Flight Card demoted to Track B (typed domain component — FlightCardView : ContentView with 8 BindableProperty fields). The scaffold ViewModel is no longer used; direct property binding replaces the factory. Scaffold count: 10 → 9. Spec page: flight-segment-card-component.html (Track B display-data reference).

Updated specs: domain-controls, architecture, index, decisions record.

DQ-10 — flight detail flight info: new scaffold component or extend FlightSegmentCardViewModel?

Resolved 2026-06-22

What we found (2026-06-22 FTDT exploration): The Figma symbol flight detail flight info (node 4325:6399, 297×154 px) appears in two places — inside the expanded flight card-og detail panel, and on FTDT/Jumpseat detail pages. It shows a single flight leg from one direction (Departure OR Arrival), not the paired A→B route of the Flight Card.

FieldFlightSegmentCardViewModelflight detail flight info
Origin✓ (e.g. "ORD")✓ (e.g. "DEN")
Destination✓ (e.g. "SFO")✗ (not shown — one-directional)
DepartureTime✓ (e.g. "12:00")
ArrivalTime
Duration✓ ("4h 30m")
FlightNumber✓ (e.g. "DL 302")
AircraftType✓ ("Boeing 737")✓ (model number, e.g. "321")
StatusBadge✓ ("ON TIME" / "DELAYED")✓ — richer: includes delay duration ("Delayed 34m")
Gate (e.g. "Gate H14") — new field
ActionLinks KCM / Jumpseat Policy — new field
DirectionLabel "Departure" or "Arrival" — new field
UpdatedTimestamp (e.g. "Updated April 21, 9:30am") — new field

Ask for engineering / architecture

(1) Is flight detail flight info a new scaffold component (e.g. FlightLegInfoViewModel) with its own typed properties (Gate, ActionLinks, DirectionLabel, UpdatedTimestamp), or should it be a richer variant of FlightSegmentCardViewModel with optional additional fields? (2) Are Gate and ActionLinks (KCM, Jumpseat Policy) available from the existing Flight/Leg domain model or do they come from a separate API? (3) Is the delay duration in the StatusBadge derived from the same data as the Flight Card's status, or is it a separate field?

Initial assessment: The 4 unique fields (Gate, ActionLinks, DirectionLabel, UpdatedTimestamp) and the fundamentally different layout intent (single-directional leg detail vs paired-route card) suggest a new scaffold component. The overlap is 5 shared fields out of 12 total — below the reuse threshold established by DQ-9. However, the final call depends on whether Gate/ActionLinks come from the same data fetch as the flight card (which could push toward extension) or a separate detail API (which confirms a separate component and factory). See D15 precedent: one domain control, multiple Figma masters — the same could apply if it's one factory producing different scaffold types.

Design answer: Track B typed ContentViewFlightLegInfoView : ContentView with BindableProperty fields. The 4 unique fields (Gate, ActionLinks, DirectionLabel, UpdatedTimestamp) and the fundamentally different layout intent confirm this is not an extension of the flight card. Decided by D17 (two-track component pattern, 2026-06-22). See D17.

DQ-11 — Compact flight rows inside card-duty period-alt: separate FlightSegmentRowViewModel or embedded in the Duty Period template?

Resolved 2026-06-22

What we found (2026-06-22 FTDT exploration): When card-duty period-alt is expanded (Variant2, 345×1051 px), the "MANAGE FLIGHTS & DETAILS" panel contains inline flight segment rows. These rows show a 3-line format per segment:

  • Date / time line (e.g. "April 28 2026 10:58 -06:00")
  • Route line: Origin Destination with a plane icon (e.g. "DEN ——✈—— MIA")
  • Detail line: "Flight Time: 00:00 DL 302 757"

These instances are named flight detail flight info in Figma and are constrained to ~178 px wide by the duty period card container. Their layout is bidirectional (Origin AND Destination) — different from the standalone flight detail flight info symbol (DQ-10) which is one-directional. Fields visible: Origin, Destination, FlightNumber, AircraftType, FlightTime/Duration — 5 fields, no StatusBadge.

Ask for engineering / architecture

(1) Should the flight rows inside a duty period card be a reuse of FlightSegmentCardViewModel rendered in a compact/embedded variant (controlled by a ConstrainedWidth or Mode property), or should DutyPeriodCardViewModel own an inner collection with its own row template? (2) Does the Duty Period CRUD model (DutyPeriodExpanded) expose segment sub-entities with the needed fields (Origin, Destination, FlightNumber, AircraftType, FlightTime)?

Initial assessment: Two viable options — (A) DutyPeriodCardViewModel owns an IEnumerable<FlightSegmentSummary> collection of lightweight row objects (inline, no separate scaffold component); or (B) reuse FlightSegmentCardViewModel with a compact rendering mode. Option A is simpler and keeps the flight segment rows as internal detail of the duty period card. Option B enables shared XAML templates but requires adding a compact-mode discriminator to FlightSegmentCardViewModel. The 5-field, no-StatusBadge subset and the 178 px constrained width favor Option A (lightweight inline rows rather than a full scaffold component).

Why it matters: determines the implementation boundary of DutyPeriodCardView and whether sub-factories are needed.

Design answer: Option A — compact flight rows are internal to DutyPeriodCardView. The 5-field, no-StatusBadge subset and 178 px constrained width do not warrant a separate scaffold component. DutyPeriodCardView owns an inner collection (IEnumerable<FlightSegmentSummary> or equivalent lightweight row type) rendered via its own embedded template. No sub-factory needed. Decided by D17 (two-track component pattern, 2026-06-22). See D17.

DQ-13 — Loading / skeleton pattern for the dynamic feed: initial load and differential refresh UX

Tentative API approach

Context: The dynamic feed loads a PageDto response. The app has a HomeSkeleton.razor / ComponentSkeleton.razor scaffold. The update strategy has been partially discussed but the skeleton UX has not been designed.

Agreed approach: client passes a since date; server returns the current PageDto (or the subset of containers updated since that date). Client compares the response against its cached state by id and resolves adds, removes, updates, and reorders itself. Server stays stateless — no diff computation, no client-state tracking required.

Flow:

ScenarioClient behaviourAPI call
First load / cold start No cache — show skeleton, fetch full PageDto, render and store with fetch timestamp. GET /page/{pageId}
Return visit (unchanged) Render from cache immediately; background refresh returns 304. GET /page/{pageId}?since={lastFetched} → 304
Return visit (content updated) Render from cache; new payload arrives; client diffs by id — inserts added containers, removes missing ones, updates changed ones, reorders as needed. GET /page/{pageId}?since={lastFetched}PageDto

Ask for design

(1) Please provide Figma frames for the initial load skeleton (full-page, cold start). (2) Should in-place container updates (delta refresh) be visually silent, or should they animate in? (3) Do container skeletons vary by type (carousel skeleton vs grid skeleton vs stack skeleton), or is there one generic placeholder shape? (4) Is there a visual treatment for "content just updated" after a delta arrives (e.g. a brief flash or no visual signal)?

Engineering dependency (Option A): The backend needs to track a page-level or container-level last-modified timestamp so it can respond correctly to ?since= queries — returning only updated containers or a 304. The client already has id on every container and item (established in the API contract), which is the reconciliation key. No server-side diff computation required.

Design answer: — pending skeleton frames and animation decision —

Cross-reference: dynamic-feed-api-contract.html §9 Open Items (incremental page updates).

DQ-12 — Spacing tokens: inferred from auto-layout node hierarchy; 3 outlier values need design sign-off

Resolved 2026-06-30

What we found: The Figma file (gpO3masyyNNHxjvRtdcRo7) has no spacing variables defined. The get_variable_defs call on the UAL home frame (node 20716:1802) returned empty. All spacing values in the current component spec are inferred or placeholder — none are sourced from Figma.

Figma references (base design — no spacing annotations yet): Home screen (node 4099:5230) · Carousel container (node 4317:5239) · card-lg (node 4033:1436) · card-md (node 4718:11125). These frames are the reference points for measuring spacing manually if variables cannot be added to the Figma file in time.

Why it matters: Spacing is a frontend concern — the frontend resolves it using its existing MEC context, so the backend does not need to carry it. But the frontend needs a canonical source of truth for the spacing scale and per-component usage before implementation. Without Figma annotations, any values baked into component styles will be guesses.

Ask for design

Please add spacing variable definitions and per-component annotations to the Figma file. Specifically needed:

  • Spacing scale — define the token set with concrete pixel values (e.g. Spacing/XSmall = 12px, Spacing/Small = 16px, etc.) as Figma variables so they can be read programmatically.
  • Container padding — annotate what spacing token applies to the internal padding of each container type: Carousel, Grid, Stack, ButtonGroup.
  • Item gap — annotate the gap between items inside each container (horizontal gap in Carousel, grid gap in Grid, vertical gap in Stack).
  • Section-to-section rhythm — annotate the vertical spacing between containers on the page.

Tentative answer (2026-06-26): Auto-layout inference sweep across 9 component nodes confirmed a strict 4 px grid. The scale below is implementation-ready for all components except Flight Card and Notification Card (3 outliers still need design confirmation). UAL MEC uses identical spacing — no MEC delta.

TokenValueKey usage
Spacing/XTiny4 pxButton card body gap, inline icon gaps
Spacing/Tiny8 pxDominant step (14×) — card top padding, card-btn outer, navBar-bottom, carousel gap
Spacing/XXSmall12 pxcard-lg / card-md content item gap, button icon-text gap
Spacing/Small16 pxPage margin (--page-margins), nav item py, card inner padding
Spacing/Medium20 pxNotification card outer gap
Spacing/Large24 pxcard-lg bottom padding, notification outer padding

Resolved (2026-06-30): The foundations reconciliation located the canonical Figma spacing scale (named 14-step scale, node 117:176 — xtiny 4 · tiny 8 · xxs 12 · xs 16 · small 20 · medium 24 · large 28 …), which supersedes this question's original "no spacing variables" premise. The 3 outliers are confirmed off that grid and are resolved by the consistency rule below; exact px confirmation rides on the reconciliation values pull (decision D-S1).

Consistency rule (adopted)

  • Reusable componentsnormalize off-grid spacing onto the canonical scale: conform to the established convention for that control type; otherwise round up.
  • Static / one-off controls → keep the value but note the exception in that control's own spec — don't force it onto the grid.
OutlierLives inReusable?Resolution
5 px — notification text gap (4864:31875)card-notification (Notifications + Comms)YesNormalize → 4 px (Spacing/XTiny)
10 px — flight-card section / route gaps (4555:7483, 4555:7000)Flight Card (FTDT + Flight Finder)YesNormalize → 12 px (Spacing/XXSmall) — conform to the card content-gap convention / round up
18 px — bottom-nav inter-tab gap (5035:16370)BottomNav (app chrome)Yes (layout)Flex-distributed — a computed result of even tab distribution (justify-content), not a spacing token. No 18 px token created; to be annotated in design-tokens' Spacing section under D-S1.

Normalizations are decisions recorded here; the live token / CSS change is applied under D-S1 alignment (gated — must not shift shipped mock layouts). Full scale in design-tokens.html — Spacing & Layout Tokens. Cross-reference: dynamic-feed-api-contract.html §9 (spacing token strategy).

DQ-14 — MEC airline fonts: which weights are needed, and are separate files being procured?

Open — weight policy settled 2026-07-29; MEC font procurement (DAL, FDX) still blocking

What we found: The Figma MEC screens use per-airline typefaces in addition to the base Futura PT. From the design captures so far:

  • UAL — League Spartan throughout (headings and body, replacing Lora/Futura PT in branded sections)
  • DAL — Bebas Neue (headings/section labels) + League Spartan (body/card text)
  • FDX — font family not yet confirmed from design capture

Why it matters — bold requires a separate font file. Each font weight is a distinct binary file. The browser/rendering engine cannot generate a true bold from a single Regular file — it will apply synthetic bold (algorithmically thickened strokes) which looks noticeably worse, especially at display sizes. For a professional app this is not acceptable.

We currently have two purchased weights for Futura PT: FuturaPT-Book.otf (Book/400) and FuturaPT-Bold.otf (Bold/700), with matching .ttf files registered natively in MauiProgram.cs. In Futura PT, "Book" is the regular weight — the family has no separate face named "Regular", so Book and Regular are two names for the same 400 file, not two files. Weights between the two do not all resolve the same way: Medium (500) resolves to Book 400, while Demi/SemiBold (600) resolves to Bold 700 — CSS font matching rounds down below 500 and up at 600 and above. Both are real purchased faces, so neither is synthesised.

For the MEC airline fonts (League Spartan, Bebas Neue, etc.), we have no purchased files yet. Before implementation can begin on any MEC screen, the correct font files must be procured — one file per weight used in the design.

Ask for design

  1. Per MEC, which font weights does the design actually use? For each airline font (e.g. League Spartan UAL, Bebas Neue DAL) — list the exact weights: Regular, SemiBold, Bold, etc. Each distinct weight = one file to procure. If the design only uses one weight of a font, we need only one file; if it uses two, we need two.
  2. Can the design be constrained to two weights per font family? Regular (400) + Bold (700) is the most practical licensing target — it maps cleanly to font-weight: 400 and font-weight: 700 in CSS, with no intermediate weights that could fall back to the wrong file. If the design currently uses Medium or SemiBold, can those be rounded to Regular or Bold?
  3. For Futura PT — does the design ever need weights other than Book and Bold? We have both purchased. Medium/SemiBold/Light would require additional licenses.
  4. FDX font family — please confirm which typeface(s) FedEx screens use (not confirmed from the current Figma capture).

Partial update (2026-06-26):

AirlineTypeface(s) confirmedFont filesStatus
UAL League Spartan (Regular 400 + Bold 700 per D33) ual.alpa.org/Admin/File-Management → ALPA App > Branding > Fonts Confirmed
DAL Bebas Neue (headings/section labels) + League Spartan (body/card text) Not yet located — pending design team delivery Partial
FDX Not yet confirmed from Figma capture Open

Questions 1–4 above still apply for each airline not yet at Confirmed. Full resolution is a procurement blocker for DAL and FDX implementation.

FDX re-checked 2026-07-06: the MEC Figma file was edited again on 2026-06-30, so the FDX page was re-scanned in full (every fill color and font on the page). Result: still no confirmed FDX typeface — the page shows a wide generic mix (League Spartan, Lora, Raleway, Futura PT, Futura, SF Pro, Merriweather, Inter) with nothing that reads as an intentional FDX choice, and zero FDX brand colors either. More Figma activity has not translated into FDX progress — this needs a direct design-team nudge, not another automated re-check.

Resolved 2026-07-29 — weight policy settled:

The base design system uses Regular/Book only. The Medium/Demi/Heavy Futura PT set previously assumed as forthcoming (see the tentative DQ-4 / DQ-8 note in naming-decisions-record.html) is not being procured. Design has accepted Regular/Book as the base text weight everywhere, which removes the licensing question for intermediate weights entirely.

The single exception is MEC customisation, which is a featured capability. A per-MEC theme may supply its own licensed typefaces and weights — that is the point of the MEC branding layer, and it is why the UAL League Spartan Regular 400 + Bold 700 pairing above remains valid. Those overrides ride on the MEC theme tokens (D33), not on base component rules.

Practical effect on the codebase: any base rule specifying an intermediate weight (500 / 600 / 800) has no real face of its own. .alpa-list-headline was corrected to font-weight: 400 under AB#2358. The remaining intermediate-weight declarations have not yet been swept — measured 2026-07-30: alpa-components.css 52 × 600, 16 × 500, 3 × 800; ftdt.css 36; widget.css 2 — 109 in total. Declarations at 400 and 700 are unaffected, since both files are purchased.

Correction (2026-07-30) — the sweep is not a visible change

This entry previously stated that intermediate weights "would render synthesised" and that the sweep "is a visible change wherever a real Bold face is currently being selected". Both are incorrect. Measured on device (iOS simulator, Blazor WebView) by rendering identical text at each weight in Futura PT and comparing widths:

Requested weightRendered widthFace actually used
300 · 400 · 500516.960 pxBook 400 — a real face
600 · 700 · 800 · 900579.400 pxBold 700 — a real face

No synthesis occurs: CSS font matching selects a real purchased face in every case. Normalising 500400 and 600/800700 therefore selects the same face that is already being drawn — the sweep is visually null and does not require per-component design sign-off. It makes the stylesheet state what already renders. See Typography Font Mapping § 3.1.

League Spartan is not MEC-only (2026-07-30). This entry frames League Spartan as per-airline MEC branding (UAL). The 2026-07-30 census found Other/Button = League Spartan Regular 400 in the base design system of the working file owEYzHf7FrHRvWC2u82UOl — the button style for button pill, tab group, card-button, profile and the saved/recent searches card. No League Spartan file ships, so those buttons render Futura PT today. Under the two-file direction that is the correct outcome, but the base-level usage is a separate fact from the MEC branding layer. See Typography Font Mapping § 2.

Raised 2026-06-26. Weight policy resolved 2026-07-29. Related: D33 (font-family as MEC theme token — pending). Figma references: UAL home node 20716:1802 · DAL home node 21247:2989 · FDX home node 21010:2758.

DQ-15 — DAL: two distinct brand navies on the same screen — intentional primary/secondary pattern or inconsistency?

Open — confirm before speccing DAL theme tokens

What we found: The DAL home screen (node 21247:2989) uses two distinct dark navies on the same screen:

  • Welcome card background#002a50 (Delta brand navy, the primary)
  • NAVIGATE grid (ButtonCard backgrounds)#09243d (noticeably darker, a different shade)

2026-07-06 update — pixel-confirmed via rendered screenshot (not just raw Figma fills), extends the pattern beyond the original two elements:

  • #002a50 also appears on the Contract Comparison featured content card, not just the Welcome card.
  • #09243d-family (sampled #0b2443, same shade within rendering tolerance) also appears on the TOOLS section cards (Scheduling Ticketing System, DART), not just the NAVIGATE grid.
  • The two tones repeating consistently across separate UI groupings (Welcome + featured-content vs. NAVIGATE + TOOLS quick-access cards) is stronger evidence for the "intentional primary/secondary pattern" hypothesis than a one-off inconsistency would produce — but this is still not a designer confirmation.
  • New sub-finding: the bottom nav bar on this same screen samples as #05273e — the base ALPA navy, neither DAL candidate. If navBar-bottom is meant to always track Surface/Brand (the rule confirmed for UAL), this screen's nav bar doesn't yet reflect that for DAL — worth asking whether this mock is simply unfinished there, or whether DAL's nav bar intentionally stays unbranded.
  • Also sampled the confirmed heart/favorite-fill red on this screen: #c01933 — close to, but distinct from, ALPA's own base "red" (#c02126, used elsewhere in this doc suite) and from Delta's real-world corporate red (#e31837). Not yet confirmed whether this is a genuine DAL-specific accent or an unrelated near-shade.

Hypothesis: This may be an intentional primary/secondary brand surface pattern — e.g. Surface/Brand = #002a50 (primary, used for header/featured-content surfaces) and a second token (e.g. Surface/Brand-Dark or Surface/Brand-Secondary) = #09243d for quick-access/tool card surfaces. If so, this is a new token slot that doesn't exist in the current theme endpoint contract and needs to be added.

Alternatively it may be a draft inconsistency — the ButtonCard grid was painted with a slightly wrong navy and should be #002a50 to match the welcome card. The now-broader, more consistent pattern (extending to TOOLS and Contract Comparison) makes this less likely than originally thought, but doesn't rule it out.

We currently have no documented primary/secondary dark-brand surface pattern in the token set. The existing Surface/Alt token is light grey (#efefef) — an elevated card surface, not a secondary brand dark.

Ask for design

On the DAL home screen (node 21247:2989), #002a50 appears on the Welcome card and the Contract Comparison featured card, while #09243d appears on the NAVIGATE and TOOLS quick-access cards. Is this a real two-surface pattern?

  • If intentional: please confirm the token name for the secondary shade (e.g. Surface/Brand-Dark) so we can add it to the theme endpoint contract and spec it as a distinct override slot for DAL (and potentially other MECs).
  • If an error: please update the NAVIGATE/TOOLS card fills to #002a50 to match the welcome/featured-content surfaces.
  • New: should navBar-bottom also carry a DAL brand color on this screen? It currently renders as base ALPA navy (#05273e), not either DAL candidate.
  • New: is #c01933 (the heart/favorite fill on this screen) a genuine DAL-specific red, or just this mock's local choice? It doesn't match Delta's real corporate red (#e31837).

Design answer: — pending —

Raised 2026-06-26. Figma node: DAL home 21247:2989. Pixel-sampling pass 2026-07-06 (screenshot-confirmed, extends context). Blocks DAL theme token spec (D35 pending).

DQ-16 — Placeholder / fallback image assets: what should display when a remote image fails to load or is absent?

Open — needed before the ALPAMobile.Presentation asset-bundling pass

What we found: Several components accept a remote Image URL from the API at runtime:

  • Hero Card (CardHeroViewModel.Image) — 200 px tall editorial photo slot · Figma node 4033:1436
  • Text Card / card-md (CardTextViewModel.Image) — optional content image · Figma node 4718:11125
  • Button Card (ButtonViewModel.Image) — icon or thumbnail for feature buttons · Figma node 4737:13038
  • Pilot / Member avatar (PilotCard.AvatarUrl) — profile photo (currently falls back to fa-circle-user; confirm this is the intended fallback or provide a branded alternative)
  • Small Card (CardSmallViewModel.Image) — compact content card image slot · node not yet confirmed from Figma capture

None of these have a defined fallback state in Figma. The Figma designs always show an image present. We do not currently have bundled placeholder assets for any of these slots. The pilot avatar currently falls back to fa-circle-user (icon-based), but the card image slots have no equivalent treatment defined.

Why it matters: Without a fallback, a missing or slow-loading remote image leaves a blank or broken image box visible to the user. This needs a design decision before the ALPAMobile.Presentation is built — either a neutral branded placeholder graphic (e.g. a lightly-tinted ALPA mark or grey rectangle with icon) or an icon-based treatment, per slot type. The assets (if graphical) must be delivered via the SharePoint folder alongside other UI Refresh assets.

Ask for design

What should display in each image slot when the remote URL is missing or fails to load?

  • Hero Card image area (200 px) — branded placeholder graphic, solid color fill from a Surface/* token, or a centered icon?
  • Text / Small Card image — same treatment or slot hidden when absent?
  • Button icon — FA icon fallback (which glyph?) or hide the icon slot?
  • Pilot avatar — currently fa-circle-user; confirm or provide a branded alternative.

If a graphical placeholder is chosen, please provide the SVG/PNG export in the SharePoint asset folder (see Asset Inventory → SharePoint Delivery Checklist).

Design answer: — pending —

Raised 2026-06-26. Affects Hero Card, Text Card, Small Card, Button Card, Pilot Card. Blocks the ALPAMobile.Presentation image-slot implementation.

DQ-17 — Icon strategy: interim Figma vectors in use; icon-font capability for the WebView still required (forward)

Open — interim vectors landed; icon-font path still open

What we found (from the AB#1821 scaffold):

  • The bottom-nav tab icons (HOME · JUMPSEAT · KCM · FTDT · MY MEC) are currently hand-drawn inline <svg> paths repeated in every Razor page — not glyphs from the design's icon set. They are visual approximations and do not match the Figma icons.
  • The project's icon fonts — materialdesignicons-webfont.ttf and fa-solid-900.ttf — live in Resources/Fonts/ for native MAUI (XAML) use only. The Blazor BlazorWebView cannot see them: they are not copied to wwwroot/fonts/ and there is no @font-face declaration or <link> in wwwroot/index.html referencing an icon font.
  • So the WebView/CSS has no icon-font surface at all — every icon in the Blazor UI is either an inline SVG path or a Unicode emoji (e.g. ♡ / 🔔 / ✕ on the saved-flight actions), which is why glyph weight and shape drift from the design.

Why it matters: the tab bar is on every screen, so an icon mismatch is the single most visible fidelity gap in the scaffold. It also blocks a clean implementation: hand-drawn SVGs cannot track the design system if the icon set is revised, and they make MEC theming of the tab bar (per-airline icon treatment) impractical.

Ask for design

  • Which icon set does the design use for the bottom nav and in-app icons — Material Design Icons, Font Awesome, or a custom ALPA icon set?
  • Delivery: provide either (a) the icon font file licensed for web/WebView use (so we can wire one @font-face into wwwroot and reference glyphs by class), or (b) SVG exports of the canonical icon set.
  • Confirm the 5 tab-bar glyphs specifically: Home, Jumpseat (airplane seat), KCM, FTDT (clock), My MEC.

Proposed interim workaround: export the 5 tab-bar icon assets (plus the high-frequency in-app icons) from Figma to wwwroot/images/icons/ as SVG/PNG and reference them directly, replacing the hand-drawn paths — a temporary measure until the icon font is confirmed and wired into the WebView. This unblocks visual accuracy without committing to a font-licensing decision. (Tracked alongside the scaffold; see Asset Inventory.)

Status update (2026-06-29) — dual strategy, kept open. The position is not "custom vectors instead of an icon font." It is two tracks:
  • Interim (current): the custom ALPA icon set is a bespoke Figma vector set (~36 named symbols on page 🖤 Icons 3:7, not FontAwesome/Material). ~29 were exported as SVG and the BottomNav tab bar now references the real Figma vectors (PR #1664) — the hand-drawn approximations are being retired. We are "limping along" on these vectors for now. Catalogued in asset-inventory.
  • Forward requirement (still open): an icon-font capability for the BlazorWebView is still needed to support scalable/extensible icon usage as the app grows (one @font-face, glyph-by-class, MEC-themeable). The interim vectors do not retire this. Licensing + wiring remain to be decided — this is the open part of DQ-17.
Full discrepancy capture: foundations-reconciliation.html#icons (alignment decision D-I1).
Proposed resolution (2026-07-06) — pending design sign-off. Confirm the custom ALPA Figma vector set (page 🖤 Icons 3:7, section "General Use Icons" 233:302) as the canonical icon family, delivered as SVG exports to wwwroot/images/icons/ and rendered via the CSS mask-image technique — MEC-themeable through background-color, so per-airline icon tinting needs no additional assets. The icon-font @font-face track is deferred, not abandoned — revisit when icon count or theming pressure demands it (that forward capability stays on the books, but it should no longer gate implementation). Licensing: none needed — these are ALPA-authored vectors. The bundled fa-solid-900.ttf / materialdesignicons-webfont.ttf are native-XAML-only and irrelevant to the WebView, so no web-font license question arises. Design's sign-off here is a yes/no on the vector set as canonical; the interim wiring (PR #1664) already matches the proposal.

Design answer (icon-font path): — pending —

Raised 2026-06-29 (AB#1821 scaffold accuracy pass); reframed 2026-06-29 to the dual interim-vector / forward-font strategy. Affects Page Template bottom nav and every screen. Related: DQ-4 (Futura PT weight gap — text fonts, distinct from icon fonts).

DQ-18 — Bottom-nav selected/active tab indicator: no design annotation for the selected state

Open — intent + base treatment captured (2026-06-30); active-state visual still a design gap

What we found: The Page Template spec documents the navBar-bottom container (bg #05273e, px-16px, pb-8px) and the five tab items, but there is no annotation in Figma for what the selected/active tab looks like — no highlighted color, underline, pill, icon-fill, or label-weight change is specified.

The reusable BottomNav component (built during the Option B chrome work, AB#2190) derives the active tab from the current route and needs a defined selected state. The scaffold currently improvises: the active item's label and icon switch to ALPA blue (.alpa-nav-item--activecolor/fill: var(--alpa-blue)) while inactive items stay white. This is a placeholder, not a confirmed design.

Ask for design

What is the canonical selected-tab treatment for the bottom navigation?

  • Color change (which token — ALPA blue, white-on-tint, accent?), and does it apply to icon, label, or both?
  • Any additional affordance — top indicator bar, pill/background, filled vs. outline icon, label weight?
  • Pressed / focus states (for keyboard + screen-reader accessibility, which Option B must wire by hand)?

Findings (2026-06-30, two sources):

  • Design intent confirmed — the Teamwork mobile requirements (Bottom Tab Bar, Space 4173) state: "the active tab is visually highlighted to reflect the user's current section." An active treatment is intended.
  • But the design artifact doesn't realize it — the canonical navBar-bottom (Figma KPK3kvOU1zPHWIovXPB6Wo, node 4099:5232, Home screen) renders all five tabs identically: white labels (#ffffff) + light-blue icons (#c7dff6) on the navy (#05273e) bar — the Home tab is not differentiated (no highlight, indicator, pill, or opacity change). The active state is presumably a NavItem State=active variant not applied in this static frame.
  • Base treatment ≠ scaffold — design base = white label + light-blue #c7dff6 icon; the scaffold improvises a dark-blue #007bc2 active treatment, which is both the wrong base color and the lower-contrast choice (≈3.3:1 on navy — below AA for the label; less prominent than the white inactive tabs).

Sharpened ask: the requirement says "highlighted" but the design doesn't show how. Design to apply/specify the NavItem active variant — which token, on icon and/or label, plus any indicator (top bar / pill / fill). Whatever is chosen must be more prominent than the white inactive tabs and meet AA on navy. Two near-term fixes regardless: (1) move the scaffold base icon to light-blue #c7dff6 to match design; (2) replace the dark-blue active placeholder.

Proposed resolution (2026-07-06) — pending design sign-off.

Evidence recap: the canonical navBar-bottom (component 4038:1037, instance 4099:5232) draws all five tabs identically — white 12 px Futura PT Medium labels, #c7dff6 28 px icons, navy #05273e bar. The active-state variant exists in intent (Teamwork Space 4173: "active tab visually highlighted") but is not drawn — this is a design-side gap.

Candidate on navy #05273eContrastVerdict
White #ffffff label (inactive base)15.37:1Pass (AAA)
Light-blue #c7dff6 icon (inactive base)11.21:1Pass (1.4.11)
Scaffold active #007bc2 label3.38:1FAILS WCAG 1.4.3 — the 12 px label needs 4.5:1; also less prominent than inactive white
Yellow #f7bb0d8.83:1Pass

Key fact: color alone cannot compliantly differentiate active from inactive — white↔#c7dff6 is only 1.37:1, and no on-navy candidate reaches 3:1 against white — so a shape affordance is required regardless of color.

Proposal: active = yellow #f7bb0d (--alpa-yellow / Border/Yellow token) on the icon plus a 2–3 px top indicator bar; the label stays white. Inactive = white label + #c7dff6 icon, per the design base above.

Scaffold implementation notes to fix when DQ-18 lands:

  • The CSS rule .alpa-nav-item--active .alpa-nav-icon { fill: … } is deadfill does nothing on a mask-image span; the active icon actually stays white.
  • Scaffold inactive icons are white, but the design base is #c7dff6 — correct alongside the active-state change.

Design answer: — active-state visual still pending; intent + base treatment captured 2026-06-30 (Teamwork Space 4173 + Figma KPK3kvOU1zPHWIovXPB6Wo) —

Raised 2026-06-29 (BottomNav component, AB#2190). Affects Page Template bottom nav and the BottomNav component. Related: DQ-17 (tab-bar icon font).

DQ-19 — Render-surface + host strategy: selective native-XAML promotion & Blazor↔XAML navigation

Open — architecture / platform

What we found:

  • WebView-rendered Blazor may miss the native-feel bar on gesture/physics/transition-heavy interactions (swipe-to-delete, pull-to-refresh, drag-reorder, scroll inertia, native transitions); static content / forms / simple lists generally feel fine.
  • Mixed mode is established as feasible — native XAML pages can host BlazorWebViews, so native + Blazor surfaces coexist in one app.
  • The component library is render-surface-agnostic (D12/D17 two-track: ViewModel + variant model + tokens, not CSS-bound) — the same specs render as Blazor or native XAML without redesign.
  • Ultimate fallback: because of that abstraction, the UI can pivot to full native XAML with no component redesign — the worst-case de-risk. The option space is a reversible spectrum: all-Blazor → mixed (XAML shell + native static, Blazor dynamic) → full native XAML, all from the same specs.
  • The AB#2190 full-bleed spike proved the MAUI Shell constrains the BlazorWebView frame, so the host model materially affects how native and Blazor compose.

Why it matters: sets the host architecture and the native/Blazor surface split — but the chrome abstraction makes it low-regret: start mixed and escalate to full-native if testers reject the Blazor feel, without discarding the component work.

Current leaning (J. Castro — to validate with design + eng, not decided): XAML-first Shell + native static content, Blazor for the dynamic-UI portion (data-driven feed/widgets), with full native XAML held as the ultimate fallback.

To resolve

  • Host model — XAML-first Shell (leaning) vs Blazor-first host; weigh the AB#2190 Shell-constrains-WebView finding.
  • Surface split — confirm static = native / dynamic = Blazor; enumerate which screens land on each side.
  • Switching between content (research) — back-stack, transitions, state preservation, theming parity moving native ↔ Blazor under the chosen host.
  • Dual-track validation — prove the abstraction maps to a real XAML render (this is what makes the full-native fallback safe to rely on).

Design/eng answer: — pending —

Raised 2026-06-29. Related: D12 · D17 · AB#2190 (chrome/host + full-bleed) · AB#2191 (AppShell/MainLayout split) · AB#2087 (Presentation Extraction) · DQ-20.

DQ-20 — Saved Search/Flights action affordance: swipe-to-delete vs visible action column

Open — instance of DQ-19

What we found: Figma renders saved rows with swipe-to-reveal delete (i-trash, native idiom); the scaffold uses a persistent visible action column (♥ save · ↻ re-search; 🔔 alerts · ✕ delete).

Why it matters: swipe fits a single destructive action and is costly to replicate in a WebView; these cards need multiple, mostly non-destructive actions, and accessibility needs a visible alternative regardless.

Leaning / baseline: visible action column as the default (multi-action + a11y + WebView fit); swipe-to-delete deferred as an optional enhancement for the destructive delete only — or moot if this surface is promoted to native XAML per DQ-19 (where swipe becomes free/idiomatic).

Design answer: — pending —

Raised 2026-06-29 (first concrete instance of DQ-19). Related: DQ-19 · Saved Search Card · Flight Segment Card.

DQ-21 — Type scale & accessibility text scaling across Blazor + native surfaces

Answered 2026-07-30 — implementation tracked on AB#2391

Design answer (2026-07-30)

Yes — the app should honour the OS text-size setting. Design also confirmed Body/Body M (18px) is the page default, which had been the second half of the same ask.

The two answers interact: because text must scale, the base is declared in relative units rather than a fixed pixel value, so settling the default alone would have been rework.

Current state (audited 2026-07-30) — text cannot be enlarged at all. 320 font-size declarations are px with zero rem/em; text-size-adjust is unset; the viewport in index.html and home.html carries maximum-scale=1.0, user-scalable=no, so pinch-zoom is disabled too; and no base font size is declared anywhere, meaning unstyled text inherits the WebView's 16px. Together these fail WCAG 1.4.4 Resize Text, and user-scalable=no is itself a flagged anti-pattern.

Load-bearing constraint: iOS WKWebView does not apply Dynamic Type to web content automatically — converting to rem alone will not satisfy the answer. Either the font: -apple-system-body WebKit hook (iOS-only behaviour) or a native bridge that reads the OS scale and pushes it into the WebView is required. The bridge is recommended, since the app ships both iOS and Android heads. Scope, including the 90 fixed height declarations that are the 200%-reflow risk surface, is on AB#2391.

Full record: typography-font-mapping.html; the question as put to design is docs/requirements/design-question-typography.md.

What we found: the two render surfaces honour OS text scaling very differently:

  • Native XAML (MAUI): nearly free — Label.FontAutoScalingEnabled is on by default, so controls honour iOS Dynamic Type / Android font-scale automatically.
  • Blazor WebView: does not honour Dynamic Type at all — WKWebView renders CSS px and ignores the user's OS accessibility text size. The current scaffold (hard-coded px) therefore fails accessibility text scaling — a likely tester/compliance flag, and one more DQ-19 evidence point.

Why it matters: text scaling is a baseline accessibility requirement; the WebView gap means it must be engineered, not assumed. It also bears on the DQ-19 surface decision (another "free in native, manual in WebView").

Recommended approach (proposed):

  • Relative, token-based type scale — named text styles → rem/em anchored to a root size (extends the D33 Font/Heading · Font/Body tokens with a size scale); surface-agnostic; do first.
  • WebView scale-bridge — read UITraitCollection.PreferredContentSizeCategory (iOS) / Configuration.FontScale (Android) natively, inject as a CSS variable (--text-scale) / root font-size, and re-apply on trait/config change (same pattern as the AB#2190 safe-area-on-rotation work). Clamp the multiplier so Larger Accessibility Sizes don't break layout.
  • Reflow, not just bigger text — text-bearing containers use min-height, never fixed height; explicit wrap-vs-truncate per component. (The 393×852 scaffold's fixed heights must become flexible — a component-spec + CLAUDE.md CSS rule.)
  • Native surfaces map the type tokens to scalable sizes (FontAutoScalingEnabled) and test.
  • Test matrix: default · largest standard · Larger Accessibility Sizes, both platforms.

To resolve / decide

  • Adopt the relative-type token scale + reflow rule now (cheap, surface-agnostic)?
  • Confirm the WebView scale-bridge as the mechanism for Blazor surfaces (+ the clamp policy for accessibility sizes).
  • Max-scale / reflow target — how far up must layouts survive (largest standard vs full accessibility range)?

Design/eng answer: — pending —

Raised 2026-06-29 (#6 accessibility pass). Related: D33 (font-family tokens) · design tokens · DQ-19 (render surface) · AB#2087.

DQ-22 — Content-item ID scheme (withdrawn)

Withdrawn — retracted premise

Withdrawn (2026-07-01): this question assumed a composite content-item id (ItemTypeId + prefixed ItemId, e.g. menu-123/doc-456/msg-789). That premise was retracted — the source table was erroneous. The real Content Contract has no composite key: content is a MenuItem (navigation) or a DocumentItem (content), served by the MobileContent API. No key-field / prefix-authority / namespace question remains.

DQ-23 — Content resolve endpoint (withdrawn)

Withdrawn — retracted premise

Withdrawn (2026-07-01): premised on the retracted composite-key contract — there is no /api/content/resolve batch resolver. Content is fetched by Scope/Category (documents, via DataManager.GetDocumentsForScope…AsyncIEnumerable<DocumentItem>) or through the Mobile Menu (MenuItem navigation), served by the MobileContent API. See Content Contract §2/§5.

DQ-24 — Content-type ↔ component compatibility

Resolved 2026-07-01

What we found: content types carry different fields, so not every type can back every component — a navigation entry has no description/date, so it can't back a full content card the way a content document can.

Resolved (2026-07-01): answered by the corrected Content Contract §4 component↔content-type mapping — MenuItem-backed navigation (card-sm, ButtonCard, TextCard) vs DocumentItem-backed content (card-lg/HeroCard, Carousel). There are exactly two content types; the earlier "message / msg-" type is dropped (retracted premise). TextCard's richer navigation display is covered by MenuItem.Description (Change 2).

Raised 2026-06-30; resolved 2026-07-01. See content-endpoint-contract §4 · backend-api-mapping §2.

DQ-25 — League Spartan font override on base-app (non-MEC) text

Open — same failure mode as DQ-1, please confirm before implementation

What we found (from the Figma source, live file): three text layers across the two newly-captured Flight Finder components render in League Spartan instead of the app's base font. Per D33, League Spartan is a MEC-theme-only font (the UAL heading/body slot) — base (non-MEC) app text should be Futura PT Book/Bold, same rule DQ-1 already established for the "Futura LT Pro" override.

Figma reference: base design — file owEYzHf7FrHRvWC2u82UOl (not the MEC customization file; see the file reference legend above). All five links open with Dev Mode inspector active.

Ask for design

Is League Spartan intentional here — a deliberate distinct "Button Label" type style separate from body copy — or is it an unintentional override that should be cleared so these layers inherit the base Font Family/Body (Futura PT) token, per the DQ-1 precedent?

Found during the 2026-07-02 Flight Finder scoped sync (D41D45). Flagged (not fixed) in design-tokens.html. Affected specs: flight-finder-advanced-search, search-secondary-button.

DQ-26 — PromoBanner (D43): is structured banner content a design requirement, or is the current single-image PageBanner adequate?

Open — backend Change 5 on hold pending answer

Context: the current shipped implementation renders the page banner as one full-bleed image (PageBanner.FeaturedImageLink) with all text baked into the pixels. Backend Change 5 proposes additive Title/Subtitle/LogoImageLink fields so the client renders real text instead.

Evidence for structured intent (from the D43 capture, naming-decisions-record): the Figma master banner (component_set 5512:12925) is built structured, not as a flat image:

Ask for design

Is that structure a requirement (MEC theming of the fill, accessibility of real text, client-formatted expiry dates) or incidental to how the mock was drawn — i.e. would a supplied flat image per campaign be adequate? Note: Dev-Mode annotations on the banner node have not been reviewed (no .fig export available) — if annotations exist for 5512:12925 they should be checked as part of answering this.

Consequence: backend Change 5 is ON HOLD pending this answer — if a flat image is adequate, Change 5 is withdrawn; if structure is required, Change 5 proceeds as documented. See the Backend Handoff Tracker.

DQ-27 — Document lists (card-notification): should read/unread state render, and how?

Open — Blazor /documents ships with the legacy dot until answered

Context: the document-list screens (Internal Comms 4565:9260 → ALP Mag 4867:34739, file R6hPjBdjSIn6946qTJasUC) render their rows as card-notification instances showing date · summary · chevronno visible unread indicator. But the same component's Notifications usage documents an unread element mapped to NotificationCenterMessage.HasNotBeenRead (notifications-screen-data-gap §2), and the legacy native documents list has always shown one: UnreadListItemViewModel.IsStatusIndicatorVisibleDocumentItem.Read, with mark-read-on-tap and mark-all-read-on-leave.

Current implementation (2026-07-09): the Blazor /documents page keeps legacy parity — an unread dot per row driven by DocumentItem.Read, with the same read lifecycle; the row's accent line renders in the navy/blue shade shown in the design regardless of state.

Ask for design

Should document lists surface read/unread state at all in the redesign? If yes, which affordance: (a) the dot, as the Notifications inbox does; or (b) the suggestion on the table (J. Castro, 2026-07-09): signal unread via the row's accent line color — a shade of red for unread (e.g. the existing --color-error #c02126 family), returning to the current blue/navy shade once read — no dot needed. Note: Dev-Mode annotations on 4867:34739 / the card-notification master have not been reviewed (no .fig export available) — check them as part of answering.

Consequence: if (b) is adopted it needs a token decision (which red) plus a card-notification/Card spec update, and the unread treatment should be unified across Notifications and document lists rather than diverging per screen.

DQ-28 — iOS safe areas: the design does not account for the Dynamic Island / status bar (top) or the home indicator (bottom)

Open — implementation policy adopted 2026-07-10; design confirmation wanted

Context: the Figma screen compositions (file R6hPjBdjSIn6946qTJasUC) are drawn as fixed frames with no allowance for iOS platform chrome: nothing reserves the status bar / Dynamic Island area at the top (~59px on Pro Max hardware) or the home-indicator gesture zone at the bottom (~34px). The Blazor WebView runs full-bleed (viewport-fit=cover in wwwroot/index.html), so any element the design places at a screen edge lands under that chrome unless the implementation compensates.

Implementation policy adopted (2026-07-10): safe-area insets are owned by the page shells, not by individual components — every shell pads with env(safe-area-inset-top/bottom) and keeps the design's dimension as the fallback/floor, so Figma-spec'd components themselves stay untouched:

Audit note (2026-07-10): a full sweep of every @page component found two shells missing bottom clearance — the FTDT sticky CTA and the tester dashboard — both fixed the same day. All 41 routes now flow through an inset-aware shell (AlpaScreen ×29, login shell ×2, FTDT shell ×8, dev dashboard ×2).

First fully measured instance: navBar-bottom (AB#2536, 2026-08-22)

A tester reported excessive navy below the tab-bar labels. Measuring it turned the general question above into a specific, numeric one, and the answer decides how every other bottom-anchored surface should behave.

What the node draws. navBar-bottom 5387:31928 is 393 × 88, positioned at y = 764 in an 852pt frame — flush to the artboard's bottom edge. Inside it, slot is 337 × 80 at x = 28, y = 0, so 8pt of bar is left beneath the content block. Each NavItem-primary is 53 × 80, five of them 18pt apart; the icon is 28 × 28 at y = 16 and the label sits at y = 52, 12pt tall. That yields 24pt below the label — 16pt inside the slot plus the 8pt beneath it.

Figma navBar-bottom node rendered at 393 by 88
Figma 5387:31928 at native 393 × 88

What each phone actually reserves. Both values below were read out of the running app, not computed by us:

Platform / deviceReservedWhat occupies itChannel
iOS 26.4 · iPhone 16 · 393 × 85234pt Home indicator plus Apple's margin around it env(safe-area-inset-bottom) = 34, an Apple constant in portrait
Android 36 · Pixel 9 · 412 × 924 · gesture nav24dp Gesture bar Real WindowInsets; Chromium reports env() as 0 here (AB#2499)
Android · 3-button navigation~48dp Back / home / recents — opaque buttons, not a thin glyph Same WindowInsets channel; not yet exercised on a device
Phone with no home indicator0 Both channels report 0; the node's 88pt is exact here

The ambiguity, stated precisely. The node's 24pt below the label can be read two ways, and the two readings differ by 26pt on an iPhone. Read as decorative padding, the reserved strip has to be added underneath it. Read as the reservation itself — drawn at the size a phone with no home indicator needs — it absorbs the strip. The node does not say which, and this is the question we need answered.

Each capture below is cropped to the tab bar alone — nothing else of the app is in frame. The striped magenta band marks the strip the OS reserves and the two chips report the measured values; both are a debug overlay we inject at capture time, not part of the app. The home indicator and the Android gesture handle in these images are the real OS glyphs, not drawn by us: the iOS ones come from video frames, because simctl still captures omit the compositor overlay while recordVideo includes it. Measured off those frames on the iPhone 16, the indicator is 142.1pt × 5pt, centred, sitting 8pt above the bottom edge, and it renders as the light-over-dark variant — RGB ~(45, 59, 68) against the bar's navy (3, 37, 61) — low contrast because iOS blends rather than painting solid white.

Option A — reservation added on top of the design's spacing. What shipped until 2026-08-22: the 80pt content block with the whole OS reservation appended. Rejected in testing; it is also 26pt off the node. It is nonetheless what Material's own navigation-bar component does.

iOS tab bar rendering at 114pt
iOS — 114pt, 50pt below the label
Android tab bar rendering at 104pt
Android — 104pt, ~40pt below the label

Option B — the node taken literally, 88pt everywhere. Shipped 2026-08-22 (PR 2155). On iOS this reproduces the node exactly — 88pt bar, 53pt slots, 18pt gaps, 28pt side insets, icon at 16pt, label at 52pt, 24pt below — and places the labels 24pt above the physical bottom edge, which is inside Apple's 34pt reserved strip. They clear the drawn home-indicator glyph and stay readable, but Apple's guidance asks for essential content to stay out of that strip and Apple's own tab bar keeps its labels roughly 34pt up. Android is unaffected: 24pt clears a 24dp gesture bar.

iOS tab bar rendering at 88pt
iOS — 88pt, labels inside the reserved strip
Android tab bar rendering at 88pt
Android — 88pt, labels clear the gesture bar

Option C — the node's 24pt treated as the reservation. This is our recommendation. Keep the rhythm above the label untouched (16pt, 28pt icon, 8pt, 12pt label = a 64pt content block) and make the region below the label whichever is larger, the node's 24pt or what the OS reserves: max(24pt, platform bottom inset). Nothing is invented — the node's own fixed 8pt simply becomes a minimum instead of a constant.

DeviceBelow the labelBar heightAgainst the node's 88pt
Android, gesture nav (24dp)24pt88ptExact
Phone with no home indicator24pt88ptExact
iPhone with home indicator (34pt)34pt98pt+10pt
Android, 3-button nav (48dp)48pt112pt+24pt, and unavoidable — the buttons are opaque
iOS tab bar rendering at 98pt under the recommendation
iOS — 98pt, labels on the boundary of the reserved strip
Android tab bar rendering at 88pt under the recommendation
Android — 88pt, byte-identical to option B

The Android capture above is the same file used for option B — the two renders are byte-identical, which is the point: the recommendation costs nothing on Android.

Why we prefer C to shipping 88pt flat: it is the node on three of the four cases above, including every Android phone in normal gesture-navigation use; it places labels where both platforms expect them without ever producing option A's empty slab; it degrades correctly on Android 3-button navigation, where a fixed 88pt would put opaque system buttons on top of the labels; and it survives future hardware, since the reservation is read from the OS rather than baked into a number. The cost, stated plainly: the bar is 10pt taller than drawn on home-indicator iPhones — the most common device our members carry — and the two platforms are no longer pixel-identical (98pt vs 88pt). If cross-platform identity matters more than platform-correct label placement, option B is the right call and we will hold it.

Measurement method: every app value is a getBoundingClientRect() and computed-style read taken from the running build on both devices, on the same screen, signed in with the same five tabs — not derived from a screenshot or read off the CSS. The striped band and the two chips in each capture are a debug overlay injected at capture time, reporting both inset channels. The iOS captures are extracted from simctl io recordVideo frames rather than simctl io screenshot stills, because the still path omits the compositor overlay and therefore the home indicator; the video path includes it, so the glyph in these images is the real one. Options A and B are shipped code; option C was applied live to produce its captures and is one CSS rule if adopted.

Can the indicator's colour be changed? iOS no, Android yes

Worth knowing before any treatment is chosen, because it bounds what design can ask for:

We currently set none of these — a search of the codebase finds no call to setAppearanceLightNavigationBars or its neighbours, so Android is on the system default. Since our bar is navy, the handle should be explicitly set to the light variant rather than left to chance. That is an engineering fix, not a design decision, and is noted here only so the asymmetry is visible: on iOS we cannot make the equivalent guarantee.

Ask for design

Confirm the intended safe-area treatment so the shells implement design intent rather than an engineering default: (a) what fills the top inset on each composition — should the page background simply extend (current behavior), or do hero/banner images bleed under the status bar? (b) should bottom-of-screen CTAs sit flush above the home indicator with extended background (current behavior), or float with a gap? (c) add safe-area guides to the master frames so future screens are drawn with the reserved zones visible. (d) For navBar-bottom specifically — is the node's 24pt below the label the home-indicator reservation, or padding on top of it? Is a 98pt bar on home-indicator iPhones acceptable in exchange for labels sitting on the safe-area boundary rather than inside it, or should 88pt be held everywhere? And can the spec state the bottom region as max(24pt, platform inset) rather than a fixed 8pt, so it covers gesture nav, 3-button nav and future hardware without another revision?

Consequence: until answered, any new full-bleed composition (e.g. the future ALPA-brand home, photo-hero screens) inherits the engineering default — background-extends, content-inset — which may not match the intended art direction.

DQ-34 — The tab bar with fewer than five slots is not specified

Open — implementation shipped 2026-08-22; design confirmation wanted

What we found: navBar-bottom 5387:31928 draws exactly five slots. The bar does not always carry five: signed out it carries three — JUMPSEAT · KCM · LOGIN (AB#2528) — and the tab set is data-driven from the menu API, so other counts are possible. The node has no variant for a reduced count, so the behaviour was an engineering choice by default.

What it used to do: the slots stretched to fill the same 337pt content width, 100.3pt each. That preserves the bar's width but reads as three unrelated controls pinned across the screen rather than one navigation group, and it pushes the outer targets away from a thumb.

What we shipped: a bar with fewer than five slots now holds the node's own 53pt slot and 18pt gap and centres the cluster — 3 × 53 + 2 × 18 = 195pt, centred, giving 99pt each side on a 393pt screen. The five-slot bar is untouched and still flexes, which is what lets its labels share slack rather than collide at larger OS text sizes (AB#2391) — a constraint the three-slot bar does not have, with 142pt of slack to spare.

Both captures below are the real signed-out bar — JUMPSEAT · KCM · LOGIN — taken on a device that has never been signed in, and cropped to the 88pt bar alone so nothing else on the login screen competes with the slot positions. Measured: stretched 100.3pt slots at 28pt side insets; clustered 53pt slots, 18pt gaps, a 195pt cluster centred 99 / 99.

Signed-out tab bar with JUMPSEAT, KCM and LOGIN stretched across the full bar width
Stretched — previous behaviour, 100.3pt slots, 28pt side insets
Signed-out tab bar with JUMPSEAT, KCM and LOGIN centre-clustered
Centre-clustered — shipped, 53pt slots, 18pt gaps, 99pt each side

The same question at tablet width, where it bites harder

The rule we shipped is count-based: it clusters a bar of four slots or fewer and leaves the five-slot bar flexing. That was sized for a phone, where flexing five slots lands within a few points of the design — 53pt on a 393pt iPhone, 56.8pt on a 412pt Pixel. A tablet breaks that assumption: the same five slots stretch to 141.2pt each, 2.7× the designed 53pt, spread across 778pt of bar.

DeviceSlotsFlexed slot widthClustered slot widthClustered side insets
iPhone 16 — 393pt553pt53pt28 / 28 (identical — nothing to fix)
Pixel 9 — 412pt556.8pt53pt37.5 / 37.5
iPad Pro 11-inch — 834pt5141.2pt53pt248.5 / 248.5
iPad Pro 11-inch — 834pt3247.3pt53pt319.5 / 319.5

Captured on an iPad Pro 11-inch (M4) simulator, iOS 26.4, 834 × 1210, data-alpa-idiom="tablet", signed in so the bar carries its full five slots. Cropped to the 88pt bar alone. The stretched capture is the behaviour before the fix; the clustered one now ships.

Five-slot tab bar on a tablet with slots stretched to 141pt
Tablet, flexed — before, 141.2pt slots across 778pt
Five-slot tab bar on a tablet centre-clustered at 53pt slots
Tablet, clustered — shipped, 53pt slots, 337pt cluster centred

Shipped 2026-08-22. On the phone the flexed bar is the design, so the count-based rule costs nothing there. On the tablet it is not: the icons drift to the edges and stop reading as one navigation group. The bar therefore clusters at any slot count on a tablet, five included.

The rule is keyed on the idiom, not on a width breakpoint:

:root[data-alpa-idiom="tablet"] .alpa-bottom-nav .alpa-nav-item { flex: 0 0 53px; }

data-alpa-idiom is already published once at startup by the head (IDeviceDisplayServiceMain.razoralpa-immersive.js) and is the seam this codebase already uses to say “tablets are not space-constrained”. A width breakpoint would be a second, parallel answer to a question that already has one — and it would not be free: a threshold set at the design's 337pt content block catches the 412pt Pixel too and would quietly move its slots from 56.8pt to 53pt, a visible change on a phone that nobody asked for. Keying on the idiom leaves every phone exactly as it renders today.

Measured after the change: iPad Pro 11-inch in landscape (1210pt) goes from 216.4pt slots to 53pt with the 337pt cluster centred 436.5 / 436.5; in portrait (834pt) from 141.2pt to 53pt centred 248.5 / 248.5. iPhone 16 unchanged at 53pt slots and 28pt insets, its three-slot bar still clustering 99 / 99; Pixel 9 unchanged at 56.8pt slots.

Ask for design

Confirm the centre-clustered treatment, or specify the reduced-count bar explicitly. If clustering is right, please add a reduced-count variant to the component so the rule is in the design source rather than only in CSS — and say whether the same rule should apply to four slots, which the menu API can also produce. And confirm the tablet behaviour now shipped: the five-slot bar clusters at 53pt on a tablet rather than stretching to 141pt (portrait) or 216pt (landscape) slots. If a tablet should instead have its own slot size rather than reusing the phone's 53pt, say so and we will spec it — the current rule reuses the phone value because the node offers no tablet variant.

Related: DQ-28 (bar height and the safe area) — same component, same work item. Work item AB#2536 · PR 2155.

DQ-35 — Terminology: "Adaptive Layout," not "responsive," for the tablet breakpoint work

Resolved 2026-08-23

Decision (2026-08-23)

The tablet work scoped under AB#2563 is Adaptive Layout, not "responsive layout" and not "smart promotion" (an earlier, imprecise working name). Standing vocabulary going forward:

  • Responsive layout — components resize/scale fluidly and continuously with viewport width.
  • Adaptive layout — every component keeps its locked spec size (no fluid resizing); the overall composition instead switches between discrete layout variants at defined breakpoints. The rule set that decides which variant applies is the (adaptive) breakpoint system.

Platform prior art, for reference: Apple's Size Classes (horizontal/vertical, each Compact or Regular) and Material's Window Size Classes (Compact/Medium/Expanded/Large/Extra-large) are both adaptive breakpoint systems in this sense — neither shares breakpoint values with the other, which is why AB#2563 also carries an explicit iOS-vs-Android convention-divergence question rather than assuming one shared rule set.

Why it matters: the distinction is load-bearing for how the breakpoint math gets calculated — locked component sizes are the fixed inputs the breakpoint rules are computed against. Calling this "responsive" would misdescribe the mechanism to anyone picking up the work later.

Related: DQ-34 (tab-bar cluster-vs-stretch behaviour at tablet width — same problem family, narrower scope) · AB#2409 (tablet audit — breakpoint set + simulator matrix, this decision's prerequisite) · AB#2563 (implementation).

DQ-36 — WebView Page (now AuthenticatedWebViewPage) immersive-landscape scope

Resolved 2026-08-27

Decision (2026-08-27)

Shipped with AB#2591 (which also deleted the legacy WebViewPage/MauiAuthenticatedWebView pair and replaced every call site with AuthenticatedWebViewPage/AuthenticatedWebViewHost). The immersive treatment joins the same standing rule as everywhere else — phone-only; tablets keep their chrome — rather than opening the scope question further. On a phone, rotating to landscape auto-hides the header (the ShowCompact=False variant's single bar — see DQ below on that variant) and the page fills edge-to-edge; rotating back to portrait restores it.

Implemented natively, not via the Blazor LandscapeAllowed/alpa-immersive.js marker DQ-36 originally asked about joining — that marker only reaches Blazor DOM chrome (.alpa-top-nav etc.), not this page's own native header. WebViewPageViewModel instead subscribes directly to IDeviceDisplayService.DisplayChanged (the same idiom/orientation snapshot BannerPageViewModel already uses to hide the CMS hero banner in landscape), forcing ShowCompact=True on entering landscape and restoring whatever it was before on returning to portrait. No tap-to-reveal timer, unlike the Blazor mechanism — the floating browser-control cluster (AB#2542) stays visible regardless of ShowCompact, so there is always a way out without needing the header back.

Related: AB#2538 (immersive landscape, original scope) · AB#2591 (this decision's implementation) · Tester Walkthrough — In-App Browser, Check 6.

Decided (for reference)

Design questions that have already been answered, kept here so reviewers can see what's settled.

ItemDecisionDateReference
DQ-3 — Welcome greeting fallback Decided No-rank fallback approved. When pilot rank is unavailable from the backend, the Welcome card shows "Welcome {FirstName} {LastName}" (full name, no rank prefix) — e.g. "Welcome Jose Johnson". When rank is present it prefixes as designed ("Welcome Captain Johnson"). This is the design sign-off on the Backend Report's "Option C". 2026-06-08 card · backend report
Favorite (heart) indicator Decided Rendered on all favoritable components (D31, 2026-06-26). The favorite feature persists as client state (IsFavorite). See also D7 (original removal, superseded). 2026-06-05 naming-decisions-record

DQ-29 — Flight status chip colors: card master shows all chips amber, token set defines per-status colors

Resolved 2026-07-29 — mapping confirmed (master repaint still outstanding)

Context: the flight card-og expanded master (4713:19985) renders every status chip — "On time" and "Delayed 34m" alike — in the same amber (#F7BB0D). The 2026-06-10 token capture, however, defines a per-status set and names the badge use explicitly: Status/On-time #00EA75 ("Badge on-time state · flight status badge"), Status/Delayed #F7BB0D ("Badge delayed"), Status/Error #C02126. The On-time token was added in that revision — the card master predates it and was never repainted.

Implementation decision (AB#2316, 2026-07-21): tokens win. Chips color by category — on-time green #00EA75, delayed yellow #F7BB0D (also the fallback for free-text carrier statuses), cancelled error red #C02126 with white text. "Early" has no token of its own and rides the on-time green as a good-outcome state — that mapping is the judgment call wanting design sign-off.

Question for design: confirm the per-status mapping (esp. Early → green), and repaint the flight card-og master's chips so the component library agrees with the token set.

Resolved 2026-07-29 (José) — the implemented mapping is correct; no code change required.

Early → green is signed off. Early and On-time both render Status/On-time #00EA75. Early has no token of its own and rides the on-time green as a good-outcome state, exactly as implemented under AB#2316.

Cross-checked against three independent sources on 2026-07-29 and all three agree, which is what closes this:

StateShipping CSSLegacy XAML AlpaTheme.xamlFigma
On-time / Early#00ea75StatusOnTime #00EA75#00EA75
Delayed (and free-text default)#f7bb0dStatusDelayed #F7BB0D#F7BB0D
Cancelled#c02126 + white textStatusError #C02126#C02126

Note on Status/Warning #B7770D: the general Status/Warning token in design-tokens.html lists "delayed-status indicators" among its usages, which reads as a competing value for the delayed chip. It is notStatus/Delayed #F7BB0D is the named per-status token and is what both Figma and the legacy XAML paint. #B7770D must not be applied to these chips: it would also fail WCAG AA at this chip's 12px size (4.14:1 on navy, 3.71:1 on white, against 4.5:1).

White text on cancelled is required, not stylistic. #C02126 gives 1.44:1 against navy body text — effectively illegible — and 5.87:1 against white. The other two chips keep navy text; yellow measures 8.83:1.

Still outstanding (design task, not engineering): the flight card-og master (4713:19985) still paints every chip amber and predates the On-time token. Repainting it removes the discrepancy that raised this DQ; until then the token set is authoritative and the code follows it.

DQ-30 — Bottom-nav icon assets: PDR has no icon of its own, MEC ships as a non-themeable raster

Open — implemented against the available assets; better assets requested

Context. The revised bottom-nav design (file owEYzHf7FrHRvWC2u82UOl, node 4436:5027 "navBar-bottom") specifies five slots — HOME · JUMPSEAT · KCM · PDR · {MEC} MEC — implemented in the cold-start tab seed under AB#2272. Three slots had a clean asset; two did not, and both were worked around rather than blocked.

1. PDR has no icon of its own. The design's PDR slot carries data-name="ftdt", and its exported path data matches the FTDT icon the app already ships (nav-ftdt.svg) to within sub-pixel rounding. PDR therefore renders the FTDT glyph today. That is likely a placeholder rather than an intentional shared icon — PDR and FTDT are different destinations and probably should not be visually identical in a five-slot bar. Ask: is a distinct PDR icon coming, or is sharing the FTDT glyph intended?

2. The MEC slot is a fixed-tint raster. The design exports that slot as ALPA-App-BottomNav-Logo 1, a 700×699 PNG. Decoded it is a flat single tint (#C4DCF6) with genuinely transparent holes for the globe grid, swoosh and wordmark, so it drops in as a background-image and matches the design exactly — no mask, filter or recolouring. That is what shipped.

The cost is that it cannot be themed. Every other nav icon is an SVG consumed through a CSS mask with background-color, so it follows the nav's colour; a fixed-tint raster cannot. The nav background tracks Surface/Brand, which per-MEC themes do override (theme.UAL.json sets #002243), so on a themed nav this one icon keeps its ALPA tint while its four siblings recolour. Ask: supply the ALPA globe as a flat monochrome SVG — single path, or silhouette plus true knockouts — so it can be masked and themed like the others; or confirm a fixed brand tint is intended and should deliberately not follow the theme.

Why the in-repo logo could not be reused. Recorded so it is not re-attempted: ALPAMobile/Resources/Images/alpa_logo_notification.svg is an ALPA globe in vector form, but it is a 36-path auto-trace with per-facet shading (28 dark paths, 8 light) rather than silhouette-plus-knockouts. Masking it flattens the mark to a solid disc. Combining its paths under fill-rule="evenodd" to cut holes was tried on 2026-07-29 and rendered unusable — globe grid lost, disc clipped. A clean mono export has to come from design; it cannot be derived mechanically from that file.

Raised 2026-07-29 (AB#2272). The bottom tab bar is still a mock pending the menu-API changes that will drive it (ShowOnTabBar / ShowOnTabBarSort), so these assets are consumed from the cold-start seed today. Figma: navBar-bottom 4436:5027. Related: DQ-17 (icon strategy), D33 (font-family as MEC theme token).

DQ-31 — KCM Policy Search: the primary action reads as disabled

Open — polish

Context. /kcm/policies has one action, SEARCH POLICIES, and it renders as small caps in grey on a grey fill. Every other primary action in the app is the blue endcap button — KCM's own crew-role filter uses it for APPLY, Flight Finder uses it for SEARCH FLIGHTS.

The effect is that the only thing you can do on the screen looks like the one thing you cannot. Nothing is functionally wrong: it is enabled and it works.

Ask: should this adopt the primary button treatment, or is a quieter action intended here for a reason we have not captured?

Resolved 2026-08-13 — mooted. /kcm/policies was purged: the screen had no backing data source and no counterpart in the legacy XAML app. Airline-policy search lives on /jumpseat/airline-policies (title-bar search state, Figma 4713:25303).

Raised 2026-08-02 from a UI/UX pass over the tester-facing areas (AB#2286). Not blocking UAT.

DQ-32 — KCM Policy Search: airline picks from a list, airport is free text

Open — polish

Context. The form pairs an AIRLINE select (defaulting to "All airlines") with an AIRPORT free-text field placeheld "Airport code or city". Flight Finder's airport fields on /jumpseat/search have typeahead over the same airport data.

Two fields sitting together behaving differently invites a member to expect suggestions in the second and get none. It may be deliberate — a policy search is coarser than a flight search — or it may be a typeahead that was never wired.

Ask: is the asymmetry intended? If not, the airport field should get the same typeahead as Flight Finder rather than a new pattern.

Resolved 2026-08-13 — mooted. /kcm/policies was purged: the screen had no backing data source and no counterpart in the legacy XAML app. Airline-policy search lives on /jumpseat/airline-policies (title-bar search state, Figma 4713:25303).

Raised 2026-08-02 from a UI/UX pass over the tester-facing areas (AB#2286). Not blocking UAT.

DQ-33 — Airline Policies: a search with no result count and no no-match state

Open — polish

Context. /jumpseat/airline-policies is a long alphabetical directory with a "Search airlines…" field. It shows no count, and typing something that matches nothing leaves the area blank rather than saying so.

KCM Airlines is the same shape and handles both: it states a no-match explicitly, quoting what was typed. The two lists teaching different lessons about the same interaction is the part worth fixing, more than either behaviour on its own.

Ask: should this adopt KCM Airlines' no-match treatment, and is a result count wanted on either or both? A count is the cheapest way to tell "the filter worked and there are three" from "the filter did nothing".

Raised 2026-08-02 from a UI/UX pass over the tester-facing areas (AB#2286). Related: the empty-state inventory, which records eleven surfaces with no empty state at all. Not blocking UAT.