icon.tsx exposes no strokeWidth override, so every icon in the app renders at lucide's default (2) — never the app's own intended visual weight.xl token). Never 28/2xl (today's home bell) or any other ad-hoc size.$textSecondary (#455D49) at rest, not $text (#131A14). Reads as quiet chrome, not "black solid filled."HeaderIconButton everywhere (bare icon, invisible 44pt hitSlop). Never Button variant="ghost" (a visible chunky 36px frame) and never a raw Pressable with a text-character glyph.MoreHorizontal) overflow for everything else — the ONLY overflow glyph, except a gear when the menu genuinely IS settings. Slot 1 may instead be ONE labelled HeaderTextAction (초대 코드, 관리 — decided 2026-09-17, U-05/U-09): 44pt, cardBody 600, $primary ink as its one affordance cue, $textInverse over a hero; a secondary navigation task only, never the page's primary action. Never three. Stateful/badged icons never collapse and keep their semantic color: bookmark saved = filled $primary green; bell/DM badges keep their amber/red tone.MainTabShell headerRight → NotificationBell24px (xl), $textSecondary, stroke 1.8 — matches the spec everywhere. Badge tone (amber = obligation / red = unread, deriveBellState) never resizes to hide its own signal.
DetailShell headerRight → SessionSaveButton(bookmark) + Share2 + MessageCircle, gap $3Two-glyph cap: Bookmark (stateful, slot 1 — never collapses) + ⋯ (slot 2). The ⋯ overflow holds 공유 and 일정 단톡방 만들기 (use-session-detail-data.ts's headerMenuActions — group chat, not a 1:1 host DM).
MainTabShell headerRight → MessageSquare(dot) + Share2 + Settings, gap $3Two-glyph cap: MessageSquare (badged, slot 1 — never collapses) + Settings (slot 2 — genuinely settings-adjacent, so gear stays gear, not ⋯). Stat-card share relocates into the screen's own content/설정, not the bar.
MainTabShell headerRight → ClubJoinHeaderAction (HeaderTextAction label=t().common.inviteCode)Decided 2026-09-17 (U-09): the manual-join entry is a labelled 초대 코드 text action, not a bare key glyph whose meaning lived only in its accessibilityLabel. Same primitive as 관리 on club detail — one text-action grammar.
DetailShell headerRight → HeaderActionsMenu (Button variant=ghost size=sm trigger, default Settings gear)Decided 2026-09-17 (U-05): officials get a labelled 관리 HeaderTextAction in slot 1 (→ ClubAdminScreen); ⋯ (MoreHorizontal, HeaderIconButton 24px/1.8/secondary) keeps 공유하기 (any member) and 나가기 (destructive, non-owner members). A plain member sees only ⋯. The unlabelled admin entry inside the menu was the H5 failure the usability audit named.
DetailShell headerRight → HeaderActionsMenu (same trigger pattern as club-detail)venueActions (venue-detail-screen.tsx): 길찾기 / 지도 보기 / 예약 사이트 / 예약 전화 — navigation + map + booking, NOT settings, 2-4 items depending on whether the venue has a booking URL/phone. Trigger is ⋯ (MoreHorizontal default), tight, 24px/1.8/secondary — never a gear. Court finder's own header action (courts-header-action.tsx) separately overrides this trigger to a MapPin glyph — a still-open third inconsistent overflow glyph, not shown as its own row here.
DetailShell headerRight → ActionButton(모두 읽음, tertiary) + HeaderIconButton(Settings)모두 읽음 moves off the bar entirely — the bar is gear-only (Toss-minimal, one glyph). The action relocates to sit next to what it acts on: a quiet right-aligned link on the 새 소식 band header, in the list content below.
DetailShell headerRight → raw Pressable, Text role=cardTitle literal '⋯' (both 1:1 and group)A real MoreHorizontal icon at 24px/1.8/secondary via HeaderIconButton, replacing the typed '⋯' character — same color, now with real touch/hitSlop semantics. Opens the participants sheet (group) or the report/block menu (1:1).
header-actions-menu.tsx's Popover.Content — what pushing ⋯ / the gear actually opens.ModalPanel action list (RN native Modal — always renders above content, never z-fights or flickers). Tamagui's JS-portal Popover is BANNED from product overflow menus — the anchored dropdown (HeaderActionsMenu) is retired entirely, not just re-skinned.BottomCtaBand; stateful toggles (bookmark) stay in the bar — neither ever moves into this menu.$error, separated from the rest by ONE divider (not a divider per row).$textSecondary; $error when destructive) + cardBody label, 44px min row height. NO per-row dividers.$4, $card bg over a dimmed scrim, ~208–224px wide, close affordance optional for a menu this small. Max ~5 rows.ModalPanel is the app's proven native-Modal overlay (STYLE-3 canon), replacing the JS-portal Popover that used to flicker, z-fight, and intermittently lose interactivity in production. header-actions-menu.tsx is already exactly the ModalPanel spec above (20px $textSecondary icons, 44px rows, ONE divider before the first destructive row, centered scrim card, default MoreHorizontal trigger).Session-detail's own proposal, now shipped as HeaderActionsMenu (a centered ModalPanel): the 2 actions the two-glyph cap moves off the bar are 공유 and 일정 단톡방 만들기 (use-session-detail-data.ts — a group chat, not a 1:1 host DM; label text below updated to match).
venueActions (venue-detail-screen.tsx) in a ModalPanel: 20px icons, 44px rows. None of these 4 are destructive, so — per rule — no divider at all.
clubActions is 3 rows — 공유하기 (Share2, any member) → 관리 (Settings icon, admin-only) → 나가기 (LogOut, destructive, non-owner members) — surfaced via header-actions-menu.tsx: a centered ModalPanel (no arrow), 20px $textSecondary icons, 44px rows, ONE divider directly before 나가기. The trigger is the component's default MoreHorizontal (⋯) — no call site overrides triggerIcon.
Today's 1:1 ⋯ already opens a ModalPanel — just a bespoke one (차단 first/$error, 신고 second/$text, both icon-less plain rows, no scrim-centered card chrome). PROPOSED converts it to the SAME canonical ModalPanel menu as every other screen, preserving today's tone assignment: 신고하기 stays default, 차단하기 stays the destructive row.
DM 그룹 채팅 예외 — the group thread's ⋯ keeps opening the participants ModalPanel (roster + leave), unchanged. That's a CONTENT surface, not an action menu, so it's exempt from every rule on this panel.
packages/ui/src/app-header.tsxpackages/ui/src/header-icon-button.tsxpackages/ui/src/header-actions-menu.tsxpackages/ui/src/icon.tsxpackages/ui/src/main-tab-shell.tsxpackages/ui/src/button.tsxpackages/ui/src/box-sizes.tspackages/app/src/presentation/components/sessions/session-save-button.tsxpackages/features/home/src/cards/notification-bell.tsxpackages/features/home/src/notification-center-screen.tsxpackages/features/sessions/src/session-detail-screen.tsxpackages/features/sessions/src/venue-detail-screen.tsxpackages/features/profile/src/profile-screen.tsxpackages/features/clubs/src/club-detail-screen.tsxpackages/features/clubs/src/shared/club-join-header-action.tsxpackages/ui/src/header-text-action.tsxpackages/features/messaging/src/dm-thread-screen.tsxpackages/app/src/config/themes/classic-grass.theme.ts