Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion apps/web/app/the-bet/the-bet.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 8 additions & 4 deletions apps/web/app/world-cup-2026/bracket.css
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
28 changes: 24 additions & 4 deletions apps/web/components/bracket/BracketBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,15 @@ export function BracketBuilder(props: BracketBuilderProps) {
<strong>{finalProgress.picked}</strong> of {finalProgress.total} picked
</span>
</div>
{/* 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. */}
<CascadeWarnings
warnings={cascaded.warnings}
currentTab="final"
onJumpToTab={(target) => setTab(target as TabId)}
mode="banner"
/>
<div className="bracket-final-layout">
<div
className="bracket-final-match km-pinch-wrap"
Expand Down Expand Up @@ -1871,6 +1880,16 @@ export function BracketBuilder(props: BracketBuilderProps) {
Tap the team you predict will advance. Slots fill in as you finish
the previous round.
</p>
{/* 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. */}
<CascadeWarnings
warnings={cascaded.warnings}
currentTab={panelId as CascadeTab}
onJumpToTab={(target) => setTab(target as TabId)}
mode="banner"
/>
<div
className="km-pinch-wrap"
ref={attachKmRefs ? kmContainerRef : null}
Expand All @@ -1889,14 +1908,15 @@ export function BracketBuilder(props: BracketBuilderProps) {
})}
</div>

{/* 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 <prior tab>"
* banner when the user is downstream of an incomplete stage. */}
{/* Tim 2026-06-05: details-only at the bottom. The contextual
* "Go to <prior tab>" 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. */}
<CascadeWarnings
warnings={cascaded.warnings}
currentTab={tab as CascadeTab}
onJumpToTab={(target) => setTab(target as TabId)}
mode="details"
/>


Expand Down
44 changes: 31 additions & 13 deletions apps/web/components/bracket/CascadeWarnings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <prior tab>" CTA.
* - `"details"`: just the collapsible list.
*/
readonly mode?: "full" | "banner" | "details";
}

/**
Expand Down Expand Up @@ -162,6 +173,7 @@ export function CascadeWarnings({
warnings,
currentTab,
onJumpToTab,
mode = "full",
}: CascadeWarningsProps): JSX.Element | null {
if (warnings.length === 0) return null;

Expand All @@ -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 (
<div className="bracket-cascade-warnings">
{banner ? (
{mode !== "details" && banner ? (
<div className="bracket-cascade-banner" role="status">
<span className="bracket-cascade-banner-text">{banner.message}</span>
<button
Expand All @@ -192,18 +208,20 @@ export function CascadeWarnings({
</div>
) : null}

<details className="bracket-cascade-details">
<summary>
{friendlyList.length === 1
? "Heads up about your picks"
: `${friendlyList.length} things still need picking`}
</summary>
<ul>
{friendlyList.map(([code, msg]) => (
<li key={code}>{msg}</li>
))}
</ul>
</details>
{mode !== "banner" ? (
<details className="bracket-cascade-details">
<summary>
{friendlyList.length === 1
? "Heads up about your picks"
: `${friendlyList.length} things still need picking`}
</summary>
<ul>
{friendlyList.map(([code, msg]) => (
<li key={code}>{msg}</li>
))}
</ul>
</details>
) : null}
</div>
);
}
Loading