diff --git a/apps/web/app/the-bet/the-bet.css b/apps/web/app/the-bet/the-bet.css index 9ff5560d..ba5c37f3 100644 --- a/apps/web/app/the-bet/the-bet.css +++ b/apps/web/app/the-bet/the-bet.css @@ -271,9 +271,13 @@ text-decoration: none !important; transition: transform 100ms ease; } +/* `!important` because `.vt-bet-body a` (the body-link colour rule + * higher up in this file) is more specific than this class on its + * own, which made the CTA render as gold text on a gold pill. + * Tim 2026-06-05. */ .vt-bet-cta-primary { background: #dca94b; - color: #15151a; + color: #15151a !important; } .vt-bet-cta-primary:hover { transform: translateY(-1px); diff --git a/apps/web/app/world-cup-2026/bracket.css b/apps/web/app/world-cup-2026/bracket.css index 23da89bf..822dee94 100644 --- a/apps/web/app/world-cup-2026/bracket.css +++ b/apps/web/app/world-cup-2026/bracket.css @@ -3228,12 +3228,16 @@ a.bracket-save-panel-cta-primary { } /* Selected: matches the .km-team.is-winner treatment - gold inner - * ring + halo + a brighter scrim so the picked tile pops. */ + * ring + halo + a brighter scrim so the picked tile pops. Tim + * 2026-06-05: bumped to a 5px solid-gold inset with a soft inner + * highlight + larger outer glow; the previous 3px ring wasn't + * reading strongly enough against the unselected tiles. */ .bracket-thirds-tile.is-selected { - border-color: rgba(220, 169, 75, 0.6); + border-color: #dca94b; box-shadow: - inset 0 0 0 3px var(--vt-gold-400, #dca94b), - 0 0 14px rgba(220, 169, 75, 0.35); + inset 0 0 0 5px var(--vt-gold-400, #dca94b), + inset 0 0 0 6px rgba(255, 220, 130, 0.35), + 0 0 24px rgba(220, 169, 75, 0.55); } .bracket-thirds-tile.is-selected::after { background: linear-gradient( diff --git a/apps/web/components/bracket/BracketBuilder.tsx b/apps/web/components/bracket/BracketBuilder.tsx index 33566cd3..23a54868 100644 --- a/apps/web/components/bracket/BracketBuilder.tsx +++ b/apps/web/components/bracket/BracketBuilder.tsx @@ -1757,6 +1757,15 @@ export function BracketBuilder(props: BracketBuilderProps) { {finalProgress.picked} of {finalProgress.total} picked + {/* Top banner: same hoist treatment as the KO rounds + * (Tim 2026-06-05) so the user sees the upstream-fix + * CTA without scrolling past the final-match card. */} + setTab(target as TabId)} + mode="banner" + />
+ {/* Tim 2026-06-05: hoist the upstream-cascade banner to + * the top of the round so mobile users see it before + * scrolling through empty slots; the details list still + * renders at the bottom of the whole tabpanel grid. */} + setTab(target as TabId)} + mode="banner" + />
- {/* Tim 2026-06-05: cascade warnings rendered through a - * dedicated component that translates engine codes to plain - * English and surfaces a contextual "go back to " - * banner when the user is downstream of an incomplete stage. */} + {/* Tim 2026-06-05: details-only at the bottom. The contextual + * "Go to " banner is hoisted to the top of each + * round panel above so it's visible without scrolling; the + * collapsible details list stays here as a reference. */} setTab(target as TabId)} + mode="details" /> diff --git a/apps/web/components/bracket/CascadeWarnings.tsx b/apps/web/components/bracket/CascadeWarnings.tsx index 8521e0fd..08874a57 100644 --- a/apps/web/components/bracket/CascadeWarnings.tsx +++ b/apps/web/components/bracket/CascadeWarnings.tsx @@ -50,6 +50,17 @@ export interface CascadeWarningsProps { * decides what "go back" means (typically `setTab(targetTab)`). */ readonly onJumpToTab: (target: BracketTabId) => void; + /** + * Which surfaces to render. Tim 2026-06-05 split the banner from + * the details list so the banner can hoist to the top of a round + * (it's the actionable bit) while the long-form details stay at + * the bottom (the curious-user reference). + * + * - `"full"` (default): renders both. + * - `"banner"`: just the contextual "go back to " CTA. + * - `"details"`: just the collapsible list. + */ + readonly mode?: "full" | "banner" | "details"; } /** @@ -162,6 +173,7 @@ export function CascadeWarnings({ warnings, currentTab, onJumpToTab, + mode = "full", }: CascadeWarningsProps): JSX.Element | null { if (warnings.length === 0) return null; @@ -177,9 +189,13 @@ export function CascadeWarnings({ const friendlyList = Array.from(uniqueByCode.entries()); const banner = pickBannerWarning(warnings, currentTab); + // In banner-only mode, render nothing when there's no upstream + // banner to show; the parent doesn't need an empty wrapper. + if (mode === "banner" && banner === null) return null; + return (
- {banner ? ( + {mode !== "details" && banner ? (
{banner.message}
); }