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 (