Foxbet · Component handover

Analysis page — design foundations

Every token, type step, spacing value, radius, elevation and layout metric that the match-analysis page actually ships with — read off the two live prototypes, not off Figma. One foundation, two pages: pre-game and in-play are the same design system; the in-play page adds exactly one token and one hero block. §10 is the complete delta.

Implemented · pre-game + in-play Measured 2026-08-05 · 1440px / 390px Source: foxbet-responsive.html · foxbet-responsive-live.html

§Contents

Assets used

The foundations layer is almost entirely CSS — colour, type, spacing, radius and elevation carry no images. These are the only assets the two pages reference for foundation-level chrome (brand mark, sport icons, flags, ad creatives); every other image belongs to a component that has its own handover (§11).

Notes

Flags are placeholders from flagcdn — in production the crest comes from the feed, in the same box. The two ad skins are native 400 × 1000 creatives and are never resampled (§8). The path case matters: every reference is lowercase assets/… — a capitalised Assets/… works on Windows and dies on the CDN.

01What this document is — and what wins

This is the foundations handover for the analysis page: the values a developer binds once and then never types again. It is written from the shipped prototypes, so it describes what the page does, not what an earlier spec intended.

LayerOwnerRule
Foundations — colour, type, spacing, radius, elevation, breakpoints This document Bind these to your design-system variables. Nothing here is copied per component.
Individual components — odds CTA, tips, form bubbles, phase, pitch, TV logos, LED wall Their own handovers (§11) Each one is the single source of truth for its own geometry. If it disagrees with this page, it wins.
Data, endpoints, formulasHANDOFF.md Not repeated here. Foundations are presentation only.
Prototypes are the visual spec. The two files below are real running code with the values already resolved. Where this document gives a number, that number is measured from them — but the file is still the authority you diff against: foxbet-responsive.html (pre-game) and foxbet-responsive-live.html (in-play).

The pages this describes

Pre-game · 390px

Shown at 65% scale. Countdown in the hero.

In-play · 390px

Shown at 65% scale. LIVE badge + score/clock instead of the countdown.

Both frames load the deployed prototypes from foxbet-analysis.pages.dev. The desktop layout only appears at ≥1200px, so open that URL in a full window to see the shell in §8 — a 390px iframe will always render the mobile layout.

02The token block — copy this verbatim

This is the literal :root of the pre-game page. Everything downstream reads through it; there are no second definitions of these values anywhere in the file.

/* ===== foxbet design system tokens ===== */
:root{
  --accent:#FD3A01;     /* Primary 500 */
  --accent2:#CB2F01;    /* Primary 600 — links / special text */
  --acc:#CB2F01;        /* alias the component handovers' CSS expects */
  --ember:#FF7700;      /* Secondary 500 */
  --gold:#FFB200;       /* Tertiary 500 */
  --dark:#1A1A1A;       /* Base 950 — dark surfaces */
  --ink:#1A1A1A;        /* Base 950 — primary text */
  --mut:#666666;        /* Base 700 — secondary text */
  --line:#E6E6E6;       /* Base 200 — borders */
  --fill:#F2F2F2;       /* Base 100 — fills */
  --page:#FFFFFF;       /* Base 50  — page */
  --win:#027A48;        /* Success 700 */
  --winsoft:#D1FADF;    /* Success 100 */
  --err:#D92D20;        /* Error 600 */
  --errdark:#912018;    /* Error 800 */
  --live:#D92D20;       /* IN-PLAY PAGE ONLY — live-state red (Error 600) */
  --eeep:#1A3362;       /* ΕΕΕΠ regulatory navy */
  /* featured bookmaker (Stoiximan) — swap per featured operator */
  --stoix:#0055FF; --stoix700:#003399; --stoix800:#002266; --stoixlt:#99BBFF;
  /* families */
  --disp:'Roboto',system-ui,-apple-system,'Segoe UI',sans-serif;
  --body:'Roboto',system-ui,-apple-system,'Segoe UI',sans-serif;
  --num :'Roboto Mono',ui-monospace,'SFMono-Regular',Menlo,monospace;
}
/* declared later, next to the tips component it serves */
:root{--prd-disp:'Roboto Condensed','Roboto Condensed Fallback',var(--body)}
The two pages' token blocks are byte-identical except for one line: --live:#D92D20 exists only on the in-play page. Nothing else about the foundations differs between pre-game and in-play — same palette, same ramp, same spacing, same shell.
--disp and --body are the same family today. They are kept as two tokens because display type is allowed to diverge later; bind both, and do not collapse them into one in your CSS.

03Colour — every token maps to a ramp step

No token is an invented hex. Each one is a step of the Foxbet ramps, so binding them to your design-system variables is a rename, not a translation.

TokenValueDS stepUsed for
--accent#FD3A01Primary 500Brand primary, CTA gradient end, active states, links on dark
--accent2 / --acc#CB2F01Primary 600Links and special text on light surfaces
--ember#FF7700Secondary 500CTA gradient start
--gold#FFB200Tertiary 500Ratings, gold rank, hero link hover
--ink / --dark#1A1A1ABase 950Primary text; app bar, desktop nav and footer surfaces
--mut#666666Base 700Secondary text, inactive tabs, paused live pill
--line#E6E6E6Base 200Every border and divider
--fill#F2F2F2Base 100Tab-strip track, inert fills
--page#FFFFFFBase 50Page and card surface
--win / --winsoft#027A48 #D1FADFSuccess 700 / 100Wins, positive deltas, won-tip state
--err / --errdark#D92D20 #912018Error 600 / 800Losses, lost-tip state, YouTube button
--live#D92D20Error 600In-play only — LIVE dot and score pill
--eeep#1A3362Regulatory navyΕΕΕΠ · 21+ strip (§12)
--stoix800#0055FF #003399 #002266 #99BBFFBookmaker brandThe featured operator's own palette — swap all four when the featured operator changes

The one gradient that matters

Every primary action on the page is the same brand gradient. It appears at two angles and that is deliberate — 135deg on boxes, 90deg on the wide sign-in pill:

background:linear-gradient(135deg,var(--ember),var(--accent));  /* Prim-Sec 45 — buttons, chips, active tabs */
background:linear-gradient(90deg,#FD3A01,#FF7700);           /* Prim-Sec 90 — sign-in pill */
background:linear-gradient(180deg,#333333,var(--dark));      /* dark chrome: desktop nav, overlays */

Non-tokenised colours that are still design-system steps

These appear as literal hexes in the page because they are used once or twice each. They are all real ramp steps — bind them, don't re-pick them:

50100 200300 500600

Primary ramp — #FFEBE6/#FFD8CC are the soft peach card backgrounds and borders (promo cards, «Σχόλια αγώνα», the empty-state medallion).

100200 300400 500600 700800 900950

Base ramp — the full grey range is in use. #CCCCCC is the placeholder hatch and the desktop footer text, #B3B3B3 the breadcrumb separator, #4D4D4D/#333333 the dark-chrome gradient stops.

Err 50Err 200 Err 600Warn 400 Suc 100Suc 600 Suc 700

Semantic. Text on a semantic background always uses the Dark step (#912018 / #7A2E0E / #054F31), never the 500/600 fill colour.

Colours that are legitimately outside the design system — do not "correct" them: the featured-bookmaker blues (#0055FF and its ramp), the ΕΕΕΠ navy #1A3362, the sponsor/operator brand colours inside the odds CTA, and the two national team colours used by the win-probability bar (they come from the feed, per team).

04Typography

Three families are loaded, and each has exactly one job. There is no fourth.

<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600;700
      &family=Roboto+Condensed:wght@700&family=Roboto+Mono:wght@400;500;700&display=swap" rel="stylesheet">
TokenFamilyWeights loadedJob
--body / --dispRoboto400 · 500 · 600 · 700All UI and copy. Confirmed as the brand font from live foxbet.gr.
--numRoboto Mono400 · 500 · 700Every number that must align or read as data: odds, scores, clocks, percentages, table figures.
--prd-dispRoboto Condensed700One job only — the tips component's display line. Falls back to --body.
One deliberate exception: .vp-reg, the regulatory line inside the verdict card, is Verdana 12.27px / 19px. That is Figma's value kept verbatim because the rendering of regulatory copy is the thing being signed off. Do not normalise it into the Roboto scale.

The type ramp, as actually used

Counted across the pre-game stylesheet. The page lives at the small end of the scale on purpose — it is a dense data page, and 12px (Text Tiny) is its workhorse:

SizeDeclarationsDS nameWhere
24px7H1 mobile / H2 desktopHero team score, biggest numbers
20px5H2 mobile / H3 desktopLive score, empty-state headline
18px6H3 mobile / H4 desktop, Text LargeCard headlines
16px17H4/H5, Text RegularBody copy, editorial paragraphs, countdown digits
14px58H6, Text SmallSecondary copy, buttons, sign-in, live clock
13px6Dense table cells
12px209Text TinyThe page's default: tabs, labels, section titles, table figures, chips, breadcrumb
11px21below the rampMicro-labels inside compact controls
10px8below the rampLegends, footnote captions
9px4below the rampTimeline minute markers only
The mobile/desktop step-down does not apply here. The design system drops most headings one step below 1200px — this page does not, because every size on it is already at or below Text Small. The type ramp is identical at 390px and 1440px (verified: no font-size is overridden inside the desktop media query). The only responsive type change is the form bubbles' label at ≤429.98px (10.5px inside a 20px bubble).

Weights — four, and only four

WeightDeclarationsUse
700 Bold233The default for anything labelled, numeric or interactive. A data page is bold by default.
600 Semibold56Secondary labels, breadcrumb, status bar
400 Regular15Running editorial copy
500 Medium10Rare — inside component handovers that specify it

font-weight:800 appears once, on the winner's goals in the in-play final score — Roboto 700 is the heaviest weight loaded, so that renders as synthetic bold. Treat it as an emphasis flag, not a fifth weight.

Line-height & letter-spacing

1 — single-line controls, pills, scores, anything vertically centred by flex 25×
1.15 – 1.35 — headings and tight two-line labels 18×
1.4 – 1.5 — UI copy and captions 10×
1.55 – 1.66 — running editorial paragraphs
16px / 17px / 20px / 24px — fixed px line-heights where a row's height is load-bearing 19×
letter-spacingApplies to
1.4pxSection titles (.secttl) — uppercase, 12px/700. This is the page's signature label style.
1pxUppercase placeholder tags, small caps chrome
.5pxNumeric strings in Roboto Mono (scores, clocks) and the status bar
.2px – .4pxTabs and buttons — and dropped to 0 on mobile tabs to buy horizontal room
-.1pxOne long team-name row. Never used to fix layout elsewhere.

Off-ramp sizes — the complete list

Eight declarations use non-integer or off-ramp sizes. All eight are intentional and each has an owner; there are no others, so anything else off-ramp in your port is a bug:

SizeSelectorWhy
12.27px.vp-regVerdana regulatory line, Figma verbatim
15px.nx-hThe «Άλλοι αγώνες» section heading — between Text Small and Text Large
13.5px.nx card title«Άλλοι αγώνες» cross-navigation cards
12.5px (×3).nx-c-n, .nx-c-t, .nx linkSame block — one notch under Text Tiny so three lines fit the card
11.5px.nx counter badgeSits on a 20px line-height badge
10.5px.formdots .fd ≤429.98pxForm bubble label inside a 20px bubble
9.5px.nx LIVE badgeSmallest label on the page

05Spacing — a 4px grid with three sanctioned exceptions

Name = px ÷ 4. The page uses the bottom of the scale almost exclusively, because everything is a dense row or a chip.

Steppxgap usesWhere
14px64Inside chips, between an icon and its label
28px105The default gap — between rows, cells, controls
312px30Card padding, block separation, section side gutters
416px6Section top padding, rail widget gap, wide-phone gutters
520px2Desktop nav internal gap, first-section breathing room
624pxDesktop shell padding and column gap

Structural padding you must reproduce

The three off-grid values, all deliberate: 6px — «Δες περισσότερα» hugs the paragraph it expands rather than floating halfway to the next block; 9px/10px — the dev-remote widgets in the desktop rail, which are prototype furniture and do not ship; 3px/2px — the mobile tab strip's own padding, tightened to fit more tabs (§9). Everything else is a multiple of 4.

06Radius

DS nameValueUsesApplied to
Radius Small4px64Flags, badges, inline markers, the ΕΕΕΠ box, the .secttl bar
Radius Regular (default)8px51Buttons, tabs, sub-tabs, inputs, icon squares
Radius Circle50%43Avatars, form bubbles, dots, medallions
Radius Large16px39Cards, panels, the tab-strip track, the desktop content frame
Radius Pill999px28Chips, pills, sign-in, primary CTAs
12px12Nested cards inside a 16px card — one step in, not a new token
Aliases to normalise on port: 1000px, 50px, 100px and 100% all appear a handful of times and mean nothing more than "pill" or "circle" — map them to --radius-pill / --radius-circle. 6px, 3px, 2px and 10px appear on sub-8px chrome (dots, hairline markers, small hatched chips); keep them literal rather than inventing tokens. 32px and 24px exist only on the phone-mockup frame, which does not ship.

07Elevation & focus

Four of the page's shadows are design-system styles; the rest are two page-local elevations reused consistently. Both are worth binding as variables because they recur.

ShadowUsesDS namePurpose
2px 2px 5px rgba(0,0,0,.05)7Shadow SmallResting card lift
4px 4px 20px rgba(253,58,1,.10)2Shadow ColorBranded elevation on emphasised cards
0 0 0 2px rgba(253,58,1,.40)10Focus State ShadowThe brand focus ring — keep it, it is the page's only focus affordance
0 4px 14px -10px rgba(80,40,10,.15)11page-localThe standard card elevation of this page — a warm, tight, mostly-negative-spread lift. Bind it once.
0 6px 16px -4px rgba(253,58,1,.6) · 0 8px 18px -6px rgba(253,58,1,.55)5page-localThe glow under a primary CTA — always brand-tinted, never neutral
0 1px 2px / 0 1px 3px rgba(0,0,0,.2–.3)15page-localHairline lift on small images: flags, crests, dots
inset 3px 0 0 <colour>6page-localThe left status rib on a row — semantic colour, done as an inset shadow so it does not affect layout
Blur: the foundations layer uses none. backdrop-filter appears nowhere in either page — if your port introduces it for a sticky bar, that is a new design decision, not a port.

08Breakpoints & the desktop shell

One responsive page, one breakpoint that matters. Below it the page is the mobile design at full width; at and above it the same content gets the foxbet.gr shell around it.

QueryRulesWhat it does
min-width:1200px19The breakpoint. Desktop nav, breadcrumb bar, widgets rail, ad skins, fixed footer; the mobile app bar and bottom nav are hidden.
max-width:1199.98px / 1199px15Mobile/tablet: full-bleed page, no shell, tightened tab strip.
min-width:430px · max-width:340px2Section gutters 16px on large phones, 12px on small ones.
max-width:1400px · 1280px2Narrow desktop: drop the ad skins, then un-centre the nav links. Rail and content stay.
max-width:429.98px1Form-bubble label steps down (§4).
prefers-reduced-motion:reduce14Every animation on the page has an off switch. Port all fourteen.
hover:hover2Hover behaviours are gated so touch devices get the tap behaviour instead.

Desktop geometry — the numbers are exact, not approximate

ElementMetricNote
Top nav64px fixed, z-index:200Height published as --dnav-h; the ad skins pin to it. Two rows: logo · centred menu · actions, then the league pills.
Nav / breadcrumb containermin(1192px, 100% - 48px)So the logo, pills and breadcrumb start on the same column as the rail.
Breadcrumb bar40pxReplaces the mobile in-column breadcrumb.
Content group1180px = 876 main + 12 gap + 292 railflex:0 0 auto — never squeezed. Direction is row-reverse: rail markup first, rendered left.
Main columnmax-width:876px, 16px radius, 1px --line borderElevation 0 24px 60px -30px rgba(0,0,0,.45).
Widgets rail292px, gap 16pxThe live site's sidebar width.
Ad skins400 × 1000 native, position:fixed under the navobject-fit:nonenever resampled. Offset calc(50% + 598px); overflowing the viewport is intended and body{overflow-x:clip} absorbs it.
Footer42px fixed, dark, carries the ΕΕΕΠ rowz-index:200.
Shell padding0 24px 64px, column gap 24px
Below 1200px the shell wrappers dissolve rather than hide: .dlayout,.dcenter{display:contents}. That is what lets one markup serve both layouts — keep it, or the mobile view inherits a stray flex context.

09Primitives — the shapes every section is built from

Section title (.secttl) — the page's signature

.secttl{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:700;
  letter-spacing:1.4px;text-transform:uppercase;color:var(--ink);margin:0 0 12px;padding:0 12px;}
.secttl::before{content:"";width:4px;height:14px;border-radius:4px;
  background:linear-gradient(var(--ember),var(--accent));}   /* vertical brand rib */

The optional «more» affordance on the right is 12px/700 in --accent, text-transform:none and letter-spacing:.2px — it opts out of the uppercase treatment.

Tabs — primary is a pill strip, secondary is a rectangle strip

Primary (.tabs/.tab)Secondary (.subtabs/.subtab)
Track--fill background, 1px --line, radius 16px, padding 4pxNo track — sits on --page, gap 4px
Item12px/700, padding 8px 16px, radius 8px, --mut12px/700, padding 4px 8px, radius 8px, 1px --line, white
ActiveBrand gradient, white textBrand gradient, white text, glow 0 6px 14px -7px rgba(253,58,1,.5)
OverflowHorizontal scroll + snap, desktop drag-to-scroll, fade + «›» affordanceHorizontal scroll, sticky under the pinned header
Mobile tighteninggap 8→4, track padding 4→3, item padding 16→6, letter-spacing .2→0, affordance 46→30px
Height stays 33px and font stays 12px on mobile tabs even though the strip was tightened. 33 is the fixed mobile button height and 12 is Text Tiny, the smallest ramp step — shrinking either would buy a few pixels by leaving the design system. Do not "optimise" this further.

The 33px mobile button rule

/* every action/CTA on mobile is exactly 33px tall — a deliberate brand choice,
   below the 44px touch-target guideline. Text is centred by flex, not by padding. */
.signin,.sheet-btn,.tab,.allgroups,.formbtn,.readmore{
  height:33px;min-height:33px;padding-top:0;padding-bottom:0;
  display:inline-flex;align-items:center;justify-content:center;line-height:1;}
This is an accepted, documented deviation from WCAG's target-size guidance — it is a brand rule, applied to the mobile design only. Flag it in QA rather than fixing it silently; if it is ever revisited, that is a design decision, not a port bug.

Sticky stack — --stickyH

Prototype-only furniture: the phone frame (.phone border/notch), the status bar, the dev remotes in the rail, the placeholder hatch (.phx) and the Super League demo button are not part of the design system and do not ship. Everything else in this section does.

10In-play delta — the complete list

The in-play page is the pre-game page with the countdown replaced by a live state. Foundations-wise it adds one token and 17 class names; there is nothing else. Anything else that differs between the two files is drift and should be reconciled.

AdditionSpec
--live:#D92D20Error 600. The only new token.
.livebadge + .lvdotPulsing dot pinned at hero top:13px left:15px, z-index:6; dot 11px, shadow 0 2px 6px rgba(0,0,0,.5). Pulse = livepulse 1.5s scaling to 3.2× and fading. Has a reduced-motion off switch.
.ls-pillPhase label. Pill, --live fill, white 12px/700, letter-spacing .5px, padding 4px 8px, embedded 5px dot.
.ls-score--num 20px/700, white, line-height 1, letter-spacing .5px. Separator at 60% white.
.ls-clock--num 14px/700, white.
.rest modifierPaused/ended phases (ΗΜΧ · ΛΗΞΗ Κ.Δ. · ΤΕΛΙΚΟ): pill and dot turn #666666 and the pulse stops. A pulsing dot on a stopped match reads as "still playing" — that is the whole point of the state.
.lv-win · .ls-clock.finΤΕΛΙΚΟ: the winner's goals go heavier, the clock drops to 70% white.
.lvwrapLive-stream/tracker panel. aspect-ratio:16/9, min-height:220px, radius 16px, 1px --line, standard card elevation. Reserves its space so the panel does not reflow when a third-party embed finally paints; an iframe inherits the box and needs no width/height of its own.
.lvrail · .lvph · .lvph-n · .glbar · .glbtn · .glrow · .glnote · .glph · .glfx*Dev remotes and the goal-effect demo — prototype only, do not ship. Listed so you can recognise and drop them.
The phase model itself is not specified here. The 8 football phases, their pill labels and the clock rules live in the Φάση αγώνα handover, which is their single source of truth. If a phase, label or clock rule changes, it changes there first.
Mirror rule: any change to the analysis page goes into both foxbet-responsive.html and foxbet-responsive-live.html. The only permitted difference is countdown vs live score/clock. A foundations change that lands in one file only is a bug in both.

11Components with their own handover

These are not specified here. Each link is the single source of truth for that component's geometry, states and variants — read the deployed handover before building, and when it disagrees with any other document, it wins.

ComponentHandoverAppears on
Odds CTA (ART-378)foxbet-odds-cta.pages.devBoth — verdict card, odds rows, sponsor slots
Predictions / Tipsfoxbet-predictions.pages.devBoth — the tip card inside the verdict box
Form bubbles (ART-379)foxbet-form-bubbles.pages.devBoth — hero, team cards, tables
Φάση αγώνα (live states)foxbet-fasi-agwna.pages.devIn-play — hero pill and every live row
TV channel logosfoxbet-tv-logos.pages.devBoth — broadcaster row. Light surfaces only.
Ενδεκάδες (the pitch)foxbet-endekades.pages.devBoth — probable lineups
«Αναλύσεις ημέρας» widgetfoxbet-analyseis-imeras.pages.devDesktop rail
ΕΕΕΠ regulatory footerfbnew-5ow.pages.dev/handover-footer-eeepBoth — mandatory (§12)

12Regulatory — non-negotiable

13Porting checklist

Foxbet · Analysis page — design foundations handover. Measured 2026-08-05 from foxbet-responsive.html and foxbet-responsive-live.html at 1440px and 390px. Component geometry lives in the per-component handovers listed in §11; data contracts and formulas live in HANDOFF.md.