From 107f5de07c01ecb78f09e124f59fa397c4fb57a5 Mon Sep 17 00:00:00 2001 From: Tim Thomas <0800tim@gmail.com> Date: Fri, 5 Jun 2026 12:04:53 +1200 Subject: [PATCH 1/2] fix(bracket+bet): heavier gold ring on selected Top 8 3rds + dark text on bet CTA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two visual bugs Tim flagged on 2026-06-05: * The selected/unselected contrast on the Top 8 3rd-placed teams tiles was too subtle. The 3px inset gold ring + rgba(.., 0.6) border read as a faint outline next to the unpicked tiles, not as an unambiguous "this one is picked" affordance. Bumped to a 5px solid-gold inset with a 1px soft inner highlight + larger outer glow (24px / 0.55 alpha vs 14px / 0.35). The picked tile now visibly lifts. * /the-bet bottom CTA ("Pick your bracket to enter →") was rendering gold text on a gold pill. The pill's class .vt-bet-cta-primary set color: #15151a but .vt-bet-body a (same file, body-paragraph link styling) is (0,1,1) specificity vs (0,1,0) and was winning the cascade. Added !important to the CTA's color rule, matching the pattern .vt-bet-cta-ghost already uses for the same reason. Refs: sessions/2026-06-05_orchestrator_bracket-save-on-exit.md Signed-off-by: Tim Thomas <0800tim@gmail.com> --- apps/web/app/the-bet/the-bet.css | 6 +++++- apps/web/app/world-cup-2026/bracket.css | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) 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( From 962e983c154125a869b8f80a415d754f774199ff Mon Sep 17 00:00:00 2001 From: Tim Thomas <0800tim@gmail.com> Date: Fri, 5 Jun 2026 12:11:20 +1200 Subject: [PATCH 2/2] fix(bracket): hoist cascade banner to top of round, keep details at bottom Tim 2026-06-05 (after the cascade-warnings English-ification ship): > You see the friendly note on desktop now, but it's not visible on > mobile. [...] move the first one to the top and leave the second > one at the bottom. The previous patch landed both the contextual banner and the details collapsible at the bottom of the whole tabpanel grid, so on mobile the banner sat below the matches and the Next-stage CTA, and users hit the empty R32 slots without seeing the explanation. Split the surface so the banner (the actionable bit) hoists to the top of each round and the final-panel section, while the details list (the curious-user reference) stays at the bottom. * CascadeWarnings: new `mode` prop, default `"full"` for back-compat. `"banner"` renders just the upstream-fix CTA; `"details"` renders just the collapsible. In "banner" mode the component returns null when no upstream stage is incomplete, so groups + thirds tabs don't paint an empty wrapper. * BracketBuilder: render `mode="banner"` inside the KO round section (just below the round help text) and inside the final-panel section (just above the final-match card). The existing bottom call is changed to `mode="details"`. Same component, two render slots. CSS untouched. Refs: sessions/2026-06-05_orchestrator_bracket-save-on-exit.md Signed-off-by: Tim Thomas <0800tim@gmail.com> --- .../web/components/bracket/BracketBuilder.tsx | 28 ++++++++++-- .../components/bracket/CascadeWarnings.tsx | 44 +++++++++++++------ 2 files changed, 55 insertions(+), 17 deletions(-) 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}
); }