brackt/app/routes/rules.tsx

218 lines
8.3 KiB
TypeScript
Raw Normal View History

import { ScoringPointsTable, QualifyingPointsTable } from "~/components/marketing/ScoringTables";
export function meta() {
return [
{ title: "Rules - Brackt" },
{
name: "description",
content: "Official rules for Brackt multi-sport fantasy leagues",
},
];
}
export default function Rules() {
return (
<div className="container mx-auto px-4 py-8 max-w-4xl">
<h1 className="text-4xl font-bold mb-2">Official Rules</h1>
<p className="text-muted-foreground mb-10">
These rules govern all Brackt leagues. Commissioners may adjust scoring
values and draft settings within the options available in league
settings, but the core structure below applies to every league.
</p>
<div className="space-y-10">
{/* Object */}
<section>
<h2 className="text-2xl font-semibold mb-1">Object</h2>
<div className="border-b mb-4" />
<p className="text-muted-foreground">
The goal is to draft athletes and teams across multiple sports, then
earn points based on how they finish at the end of their respective
seasons. The participant with the most points at the end of the
league season wins.
</p>
</section>
{/* Rosters */}
<section>
<h2 className="text-2xl font-semibold mb-1">Rosters</h2>
<div className="border-b mb-4" />
<div className="space-y-3 text-muted-foreground">
<p>
The commissioner sets which sports are included in the league and
how many draft rounds are held. The number of draft rounds must be
at least equal to the number of sports in the league.
</p>
<p>
Every participant must draft at least one team or individual from
each sport included in the league.
</p>
<p>
Any draft rounds beyond the number of sports are considered{" "}
<span className="font-semibold text-foreground">flex rounds</span>
. Picks made in flex rounds can be from any sport, including
sports that already have a dedicated roster spot filled.
</p>
</div>
</section>
{/* Scoring */}
<section>
<h2 className="text-2xl font-semibold mb-1">Scoring</h2>
<div className="border-b mb-4" />
<div className="space-y-6 text-muted-foreground">
<div>
<p className="mb-3">
Points are awarded based on where a drafted team or individual
finishes in their sport. The default point values are listed
below. Commissioners may adjust these values before the draft
New design (#309) * Redesign home page with new layout and component system - Two-column layout (My Leagues 2/3, Upcoming Events 1/3) with mobile stack - LeagueRow: square avatar, gradient draft highlight, rank/points display, progress bar - MyLeaguesCard, CreateLeagueCard with shared SectionCardHeader - UpcomingEventsCard: vertical timeline with grouped multi-league events - Shared gradient system: BracktGradients SVG defs, GradientIcon wrapper, brand.ts constants - Button default variant updated to green→cyan gradient - Navbar: plain nav links with gradient hover, support/admin icon buttons - Accessibility fixes: semantic h2 headings, aria-label on LeagueAvatar and nav elements - Storybook stories for all new components Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Responsive league row layout and mobile polish - League rows stack avatar+name on top, stats full-width below on mobile - Stats spread to right side on sm+ screens with border separator on mobile - Tighter padding on mobile (px-3/py-3), full padding on sm+ - Card headers and content use px-3 sm:px-6 to reduce mobile gutters - Two-column home layout deferred to lg breakpoint (tablet gets stacked) - Active leagues sorted by completion percentage descending - Default rank 1 / 0 points for active leagues with no scoring events yet - Fix ordinal bug for 11th/12th/13th; add aria-labels to rank change indicators - Remove dead StatDivider className prop Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Improve claude file. * Add StandingsPreview card component with podium row styling - New StandingsPreview component with gold/silver/bronze row tints for top 3, team avatar, and LeagueRow-style stat columns (Ranking + Points) with rank and 7-day point change indicators - Fix GradientIcon in Storybook by adding BracktGradients decorator to preview.tsx (renamed from .ts to support JSX) - Fix degenerate SVG gradient on horizontal strokes by switching BracktGradients to gradientUnits="userSpaceOnUse" with Lucide-space coordinates (0→24) - Revert erroneous fill: url(#gradient) from GradientIcon; stroke-only fix was sufficient once gradientUnits was corrected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update components on league homepage. * Finish up league page styling. * Work on standings page. * Add story for RecentScoresCard * Update Point Progression Chart. * Sort point progression legend by ranking and add team links to standings rows * Fix standings discrepancy on change. * Create draft cell component. * Update draft board page * Draft room improvements. * Update some draft room styling. * Fix context menu missing. * Move tab navigation and autodraft to header row, narrow sidebar * Virtualize available participants list, memoize draft room props Adds @tanstack/react-virtual to replace separate mobile/desktop lists with a single unified virtual scroll loop. Also memoizes miniDraftGrid and availableParticipantsSectionProps, and switches pick lookup from Array.find to a Map for O(1) access. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update draft room UI. * More draft room fixes. * Draft room tweaks. * Fix Rosters page. * Queue Section fixes. * Mobile Draft fixes. * Fix draft board page. * Create bracket look. * Bracket work. * Finish bracket page. * Homepage initial styling * homepage copy * Add privacy policy. Fixes #88. * how to play copy * rules copy * Fix brackets on homepage. * Add footer to website. * Glow on dots. * Landing page copy. * Fix sidebar. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 13:14:55 -07:00
begins. To find out how a specific sport determines its final
standings, visit the corresponding sport page.
</p>
<ScoringPointsTable />
</div>
<p>
If a team or individual finishes in a point-scoring position but
was not drafted by anyone, those points are not awarded to anyone.
A participant who drafted the next-highest finisher still earns
zero points for that position.
</p>
<div>
<p className="mb-3">
When two or more teams or individuals tie for a point-scoring
position within a sport, the points for all tied positions are
combined and split equally among them, rounded to the nearest
whole point.
</p>
<div className="bg-muted rounded-lg p-4 text-sm space-y-2">
<p className="font-semibold text-foreground">Examples:</p>
<p>
Tie between 2nd and 3rd: (70 + 50) ÷ 2 ={" "}
<span className="font-semibold text-foreground">
60 points each
</span>
</p>
<p>
Three-way tie for 6th, 7th, and 8th: (25 + 15 + 15) ÷ 3 ={" "}
<span className="font-semibold text-foreground">
18 points each
</span>
</p>
</div>
</div>
</div>
</section>
{/* Major-Based Sports Scoring */}
<section>
<h2 className="text-2xl font-semibold mb-1">
Major-Based Sports Scoring
</h2>
<div className="border-b mb-4" />
<div className="space-y-4 text-muted-foreground">
<p>
Some sports such as golf, tennis, and Counter-Strike are
scored across multiple major tournaments throughout the year
rather than a single season-ending event. These sports use a
qualifying point (QP) system.
</p>
<p>
At each major tournament, participants earn qualifying points
based on their finish:
</p>
<QualifyingPointsTable />
<p>
Qualifying points are not added to a participant's total league
score directly. They are used only to determine final rankings
within that sport.
</p>
<p>
Once the final major tournament is complete, qualifying points
from all majors are totaled. Those cumulative totals determine
the sport's final rankings, and the standard point values from
the Scoring section are then awarded accordingly.
</p>
<p>
Ties in qualifying points are split equally and rounded to the
nearest hundredth of a qualifying point before final rankings are
determined. The same tie-splitting logic from the Scoring section
then applies when awarding league points.
</p>
</div>
</section>
{/* Season Standings Tiebreakers */}
<section>
<h2 className="text-2xl font-semibold mb-1">
Season Standings Tiebreakers
</h2>
<div className="border-b mb-4" />
<div className="space-y-3 text-muted-foreground">
<p>
If two or more participants finish the season with the same total
score, the following tiebreakers are applied in order:
</p>
<ol className="list-decimal list-inside space-y-2 ml-4">
<li>Most 1st-place finishes across all sports</li>
<li>Most 2nd-place finishes</li>
<li>Most 3rd-place finishes</li>
<li>
This pattern continues down through each place until the tie is
broken
</li>
</ol>
</div>
</section>
{/* The Draft */}
<section>
<h2 className="text-2xl font-semibold mb-1">The Draft</h2>
<div className="border-b mb-4" />
<div className="space-y-3 text-muted-foreground">
<p>
The draft is a snake draft. Draft order reverses at the start of
each new round if you pick last in round 1, you pick first in
round 2, and so on.
</p>
<p>
If a participant's time bank runs out, the system will
automatically draft on their behalf. Autodraft picks from the
participant's pre-set draft queue first. If the queue is empty or
all queued picks have already been taken, the system selects from
the top of the available player pool.
</p>
<p>Pick trading is not allowed.</p>
</div>
</section>
{/* During the Season */}
<section>
<h2 className="text-2xl font-semibold mb-1">During the Season</h2>
<div className="border-b mb-4" />
<div className="space-y-3 text-muted-foreground">
<p>
Once the draft is complete, rosters are locked for the entire
season. There is no trading of picks or athletes between
participants.
</p>
<p>
There are no waivers or free-agent pickups. Every participant
competes with the roster they drafted.
</p>
</div>
</section>
</div>
</div>
);
}