Foxbet · Component handover

LED wall — pitch-side advertising board

Η διαφήμιση πάνω από τις ενδεκάδες, ως πινακίδα σταδίου: 130px ύψος, έξι σειρές LED, τρία σενάρια σε αυτόματη εναλλαγή. Αντικαθιστά το ticker των 38px μόνο σε desktop (≥1200px)· στο mobile δεν αλλάζει τίποτα.

Analysis page · Ενδεκάδες Concept · προς έγκριση Desktop only ≥1200px Configurable scenarios = NOT MVP

Assets που χρησιμοποιούνται

Δύο ομάδες. Οι φιγούρες του σεναρίου 2 ζουν στον φάκελο concept-assets/ και είναι placeholder stock — μπαίνουν ως mask-image, άρα από τη φωτογραφία χρησιμοποιείται μόνο το άλφα (το σχήμα), ποτέ τα χρώματά της. Τα πορτρέτα αναλυτών + το logo operator είναι τα ίδια αρχεία που ήδη χρησιμοποιεί η σελίδα ανάλυσης.

Άδειες χρήσης

Οι πέντε φιγούρες είναι royalty-free stock με ανώνυμα μοντέλα, κατεβασμένες για το prototype. Πριν από production πρέπει να αντικατασταθούν με αδειοδοτημένο ή δικό μας υλικό. Επειδή μπαίνουν ως μάσκες, η αντικατάσταση είναι αλλαγή ενός URL — δεν αγγίζει animation ή γεωμετρία, αρκεί το νέο PNG να έχει παρόμοιο aspect και διαφανές φόντο.

Ζωντανό demo

Ο ίδιος κώδικας που τρέχει στο prototype — αυτόματη εναλλαγή, ίδια timings. Η πινακίδα αποδίδεται στο πραγματικό της πλάτος (874px): οι θέσεις μπάλας/τερματοφύλακα είναι μετρημένες σε αυτό το πλάτος.

Το demo δεν εμφανίζεται εδώ. Το component είναι desktop-only (≥1200px) και το παράθυρό σου είναι πιο στενό. Άνοιξέ το σε desktop.

00Διάβασε πρώτα αυτό

Η πινακίδα είναι ένα container με N σενάρια, όχι N components. Το container ορίζει επιφάνεια, pixel grid, χρώμα χορηγού και το ρολόι της εναλλαγής· κάθε σενάριο είναι ανεξάρτητο «φιλμάκι» που ζει μέσα του.

ℹ️Πού μπαίνει. Αντικαθιστά το .adled ticker των 38px, στο ίδιο ακριβώς σημείο: αδελφός του .xiwrap, μέσα στο .bo .adledwrap, κολλητά πάνω από την ενδεκάδα. Ποτέ παιδί του .xiwrap — εκείνο είναι flex-direction:row στο desktop και θα γινόταν τρίτη στήλη ανάμεσα στις δύο ομάδες.
Desktop only. Κάτω από 1200px το .ledw-stage είναι display:none και εμφανίζεται το υπάρχον ticker. Δεν είναι responsive εκδοχή του ίδιου πράγματος — είναι δύο διαφορετικά creatives για δύο πλάτη. Η πινακίδα σε 390px δεν έχει χώρο να αφηγηθεί τίποτα.
Τα σενάρια είναι hard-coded στο MVP. Η παραμετροποίηση — να φτιάχνουμε δικά μας σενάρια χωρίς deploy — είναι φάση 2 και προδιαγράφεται στην §05. Μην την υλοποιήσετε στο MVP, αλλά μην κλείσετε τον δρόμο: η §05 λέει ποιες τρεις αποφάσεις θα την κόστιζαν.

01Η επιφάνεια — ανατομία & tokens

Πέντε στρώσεις, με αυτή τη σειρά από κάτω προς τα πάνω. Η σειρά είναι προδιαγραφή, όχι λεπτομέρεια: το pixel grid πρέπει να είναι πάνω από το περιεχόμενο.

#ΣτρώσηΚλάσηΚανόνας
1Επιφάνεια.ledw 130px ύψος, border-radius:16px 16px 0 0, γεμίζει με το χρώμα του χορηγού: linear-gradient(180deg, mix(--bk 88% #000), mix(--bk 55% #000)). Το --bk έρχεται από τους κανόνες του Odds CTA — καμία νέα παλέτα.
2Σενάρια.ledw-msgs / .ledw-msg Όλα στο DOM μαζί, στοιβαγμένα· ορατό ένα κάθε φορά μέσω clip-path.
3Λάμψη στο γρασίδι.ledw-glow 48px κάτω από την πινακίδα, top:100%, radial από το --bk, blur 7px, opacity .55. Θέλει z-index:2 στο stage — το .xiwrap έρχεται μετά στο DOM και αλλιώς ζωγραφίζεται από πάνω.
4Pixel grid.ledw-grid Δύο repeating-gradients 4px (κελιά) + ένα 21.6px (αρμοί σειρών = 130÷6). Πάνω από το περιεχόμενο (z-index:6): τα κενά κόβουν τα γράμματα και τις φιγούρες, και έτσι διαβάζονται σαν φτιαγμένα από την πινακίδα, όχι τυπωμένα πάνω της.
5Ανάκλαση.ledw-sweep Μία διαγώνια λάμψη ανά ~8.5s. Καθαρά ατμόσφαιρα — μπορεί να φύγει χωρίς να χαλάσει τίποτα.

Προαιρετικά, με class στο stage: .tilt = rotateX(6deg) με perspective:900px στον γονέα· .nodots, .noglow = απενεργοποίηση των αντίστοιχων στρώσεων.

Η «θέα από την κερκίδα» είναι, κατά σειρά σημασίας: η λάμψη που πέφτει στο γρασίδι, η ανάκλαση που περνά, και μόνο μετά η κλίση. Η κλίση μόνη της διαβάζεται σαν λάθος· η λάμψη είναι αυτή που σε βάζει στη θέση του θεατή.
Light operators. Σε ανοιχτά γεμίσματα (interwetten, pamestoixima, winmasters) μπαίνει .is-lt στο .ledw και κάθε στρώση με μελάνι γυρίζει σε σκούρο κείμενο — ο ίδιος κανόνας που ισχύει στο Odds CTA. Το ίδιο και το pixel grid, που από λευκό γίνεται μαύρο.

02Η εναλλαγή — μοντέλο & timing

Ένα JS ρολόι, όχι ένα μεγάλο CSS keyframe. Κάθε σενάριο έχει δικό του εσωτερικό timeline που ξαναξεκινά από το καρέ 0 κάθε φορά που έρχεται η σειρά του.

ΒήμαΤι γίνεταιΓιατί έτσι
Είσοδος/έξοδος clip-path transition με steps(14), 0.42s. Το σενάριο που φεύγει παίρνει .is-out, αυτό που έρχεται .is-on. Το stepped timing το κάνει να διαβάζεται σαν ανανέωση πινακίδας κολόνα-κολόνα, όχι σαν slide ιστοσελίδας. Οι δύο κατευθύνσεις εναλλάσσονται.
Restart classList.remove('is-on')void el.offsetWidthclassList.add('is-on'). Χωρίς το forced reflow ο browser συγχωνεύει τις δύο αλλαγές σε καμία αλλαγή και τα animations δεν ξαναρχίζουν — μπαίνεις στη μέση ενός loop.
Διάρκειες {a:9000, f:9000, e:8000} ms, ανά σενάριο. Η διάρκεια είναι ιδιότητα του σεναρίου, όχι σταθερά του container — γι' αυτό είναι map και όχι ένας αριθμός.
Pause.ledw-stage:hover .ledw *animation-play-state:paused. Ο μόνος τρόπος να δει κάποιος ένα καρέ σε ένα loop 26s.
ℹ️Η εναλλαγή δεν σταματά ποτέ μόνη της. Ένα manual replay (dev panel) ξαναρχίζει τον κύκλο από εκείνο το σενάριο — δεν παρκάρει. Μόνο το prefers-reduced-motion σταματά το ρολόι.

03Τα τρία σενάρια του MVP

Εγκεκριμένα από τον Βασίλη (2026-08-02) μέσα από έξι που δοκιμάστηκαν. Τα υπόλοιπα τρία (photo-save, Golden Sub, Goal) υπάρχουν στα τοπικά αρχεία concept και δεν ships.

#ΣενάριοΔιάρκειαΤι παίζει
1Sponsor .m-a9s (3×3s) Το logo του operator «χτίζεται» από pixels με τρεμόπαιγμα → σλαμάρει η απόδοση (Ολλανδία — Ιαπωνία · 1 · 1.85) → CTA ΠΑΙΞΕ ΝΟΜΙΜΑ* με τη γραμμή ΕΕΕΠ. Οι τρεις σκηνές κάνουν κοπή, όχι fade — πινακίδα σταδίου.
2Η επέμβαση .m-f9s Μία συνεχής φάση: ο επιθετικός μπαίνει τρέχοντας σπρώχνοντας τη μπάλα, πατάει, σουτάρει· ο τερματοφύλακας είναι στη γραμμή του από το καρέ 0, μαζεύεται και πετάγεται· η μπάλα φεύγει μόνο όταν τη χτυπήσει το πόδι και σβήνει μέσα στα γάντια. Κείμενο: «Θα κρατήσει το μηδέν;» + Goal 1.72 / No Goal 2.05.
3Το προγνωστικό .m-e8s Αριστερά οι δύο αναλυτές ως LED μετάλλια (δικό τους dot-matrix 3px, πιο πυκνό από της πινακίδας) που ανάβουν με τρεμόπαιγμα, ο ένας μισό χτύπο μετά τον άλλο· δεξιά το tip 1 & Over 2.5 @ 3.10· από πάνω χρυσά pixels που πέφτουν.
Κανόνας εμφανίσιμων: κόκκινο = η ομάδα μας, λευκό = ο αντίπαλος, μπλε = τερματοφύλακας. Στο σενάριο 2 της σελίδας Ολλανδία–Ιαπωνία: Ιαπωνία (μπλε) σουτάρει, Ολλανδία (πορτοκαλί) κρατά το μηδέν, γιατί το tip της σελίδας είναι «1».

04Οι φιγούρες — μάσκες, όχι φωτογραφίες

Κάθε παίκτης είναι το άλφα μιας φωτογραφίας ως mask-image πάνω σε επίπεδο χρώμα. Έτσι η ανατομία μένει πραγματική (δεν είναι stick figures) αλλά η απόδοση είναι της πινακίδας — και το «εμφανίσιμο» γίνεται με CSS.

/* το σχήμα από τη φωτογραφία, το χρώμα από την ομάδα */
.ledwsil{ background:currentColor;
          -webkit-mask-image:url(concept-assets/keeper-ready.png);
                  mask-image:url(concept-assets/keeper-ready.png);
          mask-size:contain; mask-repeat:no-repeat; mask-position:center bottom; }

/* ΕΜΦΑΝΙΣΙΜΟ = hard-stop gradient που τον κόβει η μάσκα:
   φανέλα / σορτσάκι / κάλτσες. Τα stops είναι ανά πόζα — κάθε μάσκα έχει
   δική της γεωμετρία· η βουτιά τρέχει 90deg γιατί το σώμα είναι οριζόντιο. */
.m-f .fr1{ color:#2650C8;   /* Ιαπωνία */
  background:linear-gradient(180deg,#2650C8 0 50%,#F3F3F3 50% 65%,#2650C8 65% 100%); }
.m-f .lp2-dive{ color:#FF6A00; /* Ολλανδία, οριζόντιο σώμα */
  background:linear-gradient(90deg,#FF6A00 0 36%,#F3F3F3 36% 54%,#FF6A00 54% 100%); }

Η κίνηση είναι flipbook + ταξίδι, όπως δουλεύει μια αληθινή περιμετρική πινακίδα:

ΦιγούραΠόζεςΜηχανισμός
Επιθετικόςrun-arun-bshooter-white Δύο πλάγιες δρασκελιές εναλλάσσονται με steps(1) ανά 0.3s (~6.6fps) μέσα σε wrapper που ταξιδεύει, με bob ανά βήμα· στο πάτημα η πόζα σουτ αντικαθιστά τις δρασκελιές.
Τερματοφύλακαςkeeper-readykeeper-dive Πλάγια στάση ετοιμότητας (ίδια οπτική γωνία με τη βουτιά — αυτό δίνει τη συνέχεια), μικρό σάλεμα, λύγισμα γονάτων, και η βουτιά ξεκινά γερμένη και χαμηλά και ισιώνει στο τέντωμα.
Οι θέσεις της μπάλας είναι μετρημένες, όχι «ωραίο τόξο». Το keyframe στο 33% είναι τα γάντια στο πλάτος των 874px. Αν αλλάξει το μέγεθος του τέρματος, του τερματοφύλακα ή το πλάτος της πινακίδας, ξαναμετρήστε — αλλιώς το φλας σκάει στον αέρα και η φάση διαβάζεται σαν γκολ.

05Παραμετροποίηση — δικά μας σενάρια NOT MVP · φάση 2

Ο στόχος: να φτιάχνουμε σενάρια χωρίς deploy — άλλο creative ανά χορηγό, ανά άθλημα, ανά αγώνα, ανά καμπάνια. Το MVP παραμένει τρία hard-coded σενάρια· αυτή η παράγραφος είναι η προδιαγραφή του επόμενου βήματος, ώστε το MVP να μην την αποκλείσει.

Το μοντέλο: ένα σενάριο = διάρκεια + λίστα από layers. Κάθε layer έχει τύπο, περιεχόμενο, θέση και ένα timeline από keyframes. Ο renderer δεν ξέρει τίποτα για ποδόσφαιρο — ξέρει layers.

{
  "id": "save-clean-sheet",
  "duration": 9000,
  "sport": "football",            // φίλτρο: πού επιτρέπεται
  "weight": 3,                    // βάρος στη ρότα (0 = ανενεργό)
  "layers": [
    { "type": "prop",   "art": "goal-frame", "at": {"right":14,"top":6,"w":190,"h":118} },

    { "type": "figure",                       // φιγούρα με μάσκα
      "poses": { "idle":"keeper-ready.png", "act":"keeper-dive.png" },
      "kit": "$match.home.kit",             // binding, όχι σταθερά
      "at": {"right":52,"bottom":10,"h":80},
      "track": [ {"t":0,"pose":"idle"}, {"t":.25,"crouch":true},
                 {"t":.30,"pose":"act","x":-38,"y":40,"rot":30},
                 {"t":.33,"x":0,"y":0,"rot":0} ] },

    { "type": "ball", "track": [ … ], "endsIn": "gloves" },
    { "type": "flash", "at_t": .33 },

    { "type": "copy", "in": .38, "align": "left",
      "eyebrow": "Goal / No Goal",
      "title":   "Θα κρατήσει το μηδέν;",
      "odds": [ {"label":"Goal",   "value":"$odds.btts.yes"},
                 {"label":"No Goal","value":"$odds.btts.no"} ] }
  ]
}

Τύποι layer — το MVP ήδη υλοποιεί και τους έξι, απλώς hard-coded. Η φάση 2 τούς κάνει data:

typeΤι είναιΉδη υπάρχει ως
logoΛογότυπο operator με το τρεμόπαιγμα «ανάμματος».ledw-logo
copyEyebrow + τίτλος + υπότιτλος ή chips αποδόσεων.lp-tx, .lp2-odds
oddsΜεμονωμένη απόδοση/επιλογή σε μεγάλο μέγεθος.ledw-odds
figureΦιγούρα με πόζες + εμφανίσιμο + track.ledwsil, .silf
propΤέρμα, μπάλα, πίνακας αλλαγής, κονφετί.ledw-goal, .ledw-ball, .lp5-conf
portraitΠρόσωπο σε LED μετάλλιο (αναλυτές).lp5-av

Bindings — γιατί ένα σενάριο δεν είναι εικόνα αλλά πρότυπο:

TokenΔίνει
$match.home.kit / $match.away.kit Τα δύο χρώματα εμφανισίμου — ίδιο σενάριο, σωστές ομάδες σε κάθε αγώνα.
$odds.<market>.<selection> Τιμή από το feed. Αν λείπει, το chip δεν αποδίδεται — ποτέ κενό κουτί ή «—».
$tip.pick / $tip.odd / $tip.analysts[] Το προγνωστικό και οι αναλυτές της σελίδας — ό,τι δείχνει ήδη το verdict box.
$sponsor Ο operator: γέμισμα --bk, logo, is-lt. Ήδη λειτουργεί στο MVP.
ℹ️Ρότα & στόχευση (φάση 2). Η σελίδα ζητά «δώσε μου σενάρια για αυτό το context» και παίρνει λίστα: φίλτρο σε sport / phase (pre-game vs live) / sponsor / ημερομηνίες καμπάνιας, ταξινόμηση με weight. Το σενάριο «Golden Sub» π.χ. έχει νόημα μόνο όσο τρέχει η προσφορά.
Οι τρεις αποφάσεις που θα έκλειναν τον δρόμο — αυτές μόνο ζητάμε να τηρηθούν στο MVP:
1. Μην βάλετε τα timings μέσα σε ένα μονολιθικό keyframe. Κάθε σενάριο κρατά δικό του timeline και δική του διάρκεια στο DUR map.
2. Μην κάνετε hard-code τα χρώματα εμφανισίμου μέσα στις μάσκες. Είναι background + currentColor στο layer, ώστε αύριο να τα δίνει το feed.
3. Κρατήστε τον renderer αδιάφορο για το περιεχόμενο. Το .ledw-msg δεν ξέρει τι είναι «σουτ» — ξέρει «layers που κινούνται». Όποιος βάλει λογική αγώνα μέσα στο container, ξαναγράφει τη φάση 2 από την αρχή.

Τι θα χρειαστεί επιπλέον στη φάση 2 (εκτός scope τώρα): αποθήκη σεναρίων + απλός editor/preview, validation του JSON, καταγραφή impressions/clicks ανά σενάριο, και προεπισκόπηση σε πραγματικό πλάτος 874px πριν δημοσιευτεί.

06Προσβασιμότητα

ΘέμαΤι κάνει το component
Κίνηση prefers-reduced-motion:reduceόλα τα animations σβήνουν, η ρότα σταματά και η πινακίδα παρκάρει στη σκηνή του CTA, πλήρως αναγνώσιμη. Δεν είναι «λιγότερη κίνηση», είναι καθόλου.
Φλας Το φλας της επαφής είναι 2 καρέ σε ~9s — πολύ κάτω από τα 3 αναλαμπές/δευτερόλεπτο (WCAG 2.3.1). Μην το κάνετε repeat σε νέο σενάριο χωρίς να ξαναμετρήσετε.
Κείμενο Όλα τα κείμενα είναι πραγματικό DOM, όχι εικόνες — μεταφράζονται και διαβάζονται. Οι φιγούρες, το τέρμα και η μπάλα είναι aria-hidden: είναι διακόσμηση.
Ο σύνδεσμος Ένα <a> τυλίγει όλη την πινακίδα με aria-label που λέει operator + αγώνα + απόδοση + «Παίξε νόμιμα» — γιατί οι σκηνές εναλλάσσονται και ο χρήστης screen reader δεν «προλαβαίνει» καμία. :focus-visible = χρυσό outline.
Αντίθεση Λευκό κείμενο μόνο σε σκούρα γεμίσματα· στα ανοιχτά ο κανόνας .is-lt. Το pixel grid ρίχνει ~30% φωτεινότητα — μετρήστε την αντίθεση με το grid πάνω, όχι χωρίς.

07Απόδοση

08Markup & πού συνδέεται

<div class="bo adledwrap">
  <a class="adled bo-stoiximan" data-adbrand …>…</a>   <!-- mobile ticker, μένει -->

  <div class="ledw-stage tilt" data-ledw>              <!-- desktop board -->
    <a class="ledw bo-stoiximan" data-ledwbrand href="…" aria-label="…">
      <span class="ledw-msgs">
        <span class="ledw-msg m-a" data-m="a">…</span>
        <span class="ledw-msg m-f" data-m="f">…</span>
        <span class="ledw-msg m-e" data-m="e">…</span>
      </span>
      <span class="ledw-grid"  aria-hidden="true"></span>
      <span class="ledw-sweep" aria-hidden="true"></span>
    </a>
    <span class="ledw-glow" aria-hidden="true"></span>
  </div>
</div>
ℹ️Ο χορηγός δεν λύνεται δεύτερη φορά. Στο prototype η πινακίδα αντιγράφει την κλάση bo-* του ticker μέσω MutationObserver, αντί να διαβάζει μόνη της το remote. Έτσι πινακίδα, pinned banner και σήμα στο κέντρο του γηπέδου δεν μπορούν να διαφωνήσουν. Στο production, όποιος κι αν είναι ο πάροχος του sponsor, μία πηγή — μην προσθέσετε δεύτερη.
Το .xiwrap χάνει τις πάνω γωνίες του. Η πινακίδα κρατά border-radius πάνω και το γήπεδο γίνεται 0 0 16px 16px — τα δύο διαβάζονται σαν ένα αντικείμενο. Ισχύει ήδη για το ticker· απλώς μην το χαλάσετε.

09Τι είναι MVP και τι όχι

ΣτοιχείοMVPΣημείωση
Η επιφάνεια (grid, glow, sweep, tilt, brand fill) ΝΑΙ
Τα 3 σενάρια + αυτόματη εναλλαγή ΝΑΙHard-coded, με δικό timeline το καθένα.
Sponsor από μία πηγή, is-lt ΝΑΙΉδη δουλεύει.
Αποδόσεις/tip από το feed ΜΕΡΙΚΩΣ Στο MVP οι τιμές είναι στο markup. Δώστε τις από το ίδιο μοντέλο που τροφοδοτεί το verdict box — δεν θέλουμε την πινακίδα να λέει άλλη τιμή από την κάρτα.
Παραμετροποιήσιμα σενάρια (JSON, editor, στόχευση) ΟΧΙ§05 — φάση 2.
Mobile εκδοχή της πινακίδας ΟΧΙΤο ticker μένει ως έχει κάτω από 1200px.
Αδειοδοτημένο φωτογραφικό υλικό ΟΧΙPlaceholder stock — αλλαγή URL, βλ. Assets.
Ζωντανό prototype: foxbet-analysis.pages.dev/ledwall (και οι σελίδες ανάλυσης: pre-game · live).

Foxbet · Analysis page · LED wall. Concept — προς έγκριση. Τα σενάρια είναι hard-coded στο MVP· η παραμετροποίηση (§05) είναι φάση 2 και το MVP δεν πρέπει να την αποκλείσει.