PageSourceSearch

https://www.fa-cupfinals.co.uk/js/site-nav.js?v259459737353629

js fa-cupfinals.co.uk collected 2026-10-02 10:31:52 UTC 69,373 bytes, 1,694 lines download raw bytes

1/* nav-build:259459737353629 */
2/*
3 * site-nav.js - Site-wide hamburger navigation.
4 *
5 * Builds a top-right hamburger drawer below 900px, leaves the existing
6 * horizontal nav alone above 900px. The drawer's menu structure is
7 * hardcoded in NAV_STRUCTURE below so every section (FA Cup, Premier
8 * League, World Cup, Betting) gets its own expandable submenu no matter
9 * which page the visitor is currently on.
10 *
11 * Open / close animation timing (.22s ease, height-from-scrollHeight
12 * sub-toggle pattern) mirrors the twinshair.com hamburger we copied as a
13 * reference - keeps the polished feel without rolling our own physics.
14 *
15 * Drawer styling lives in /css/style.css under the "Hamburger nav" heading
16 * so DevTools can attribute every rule to a real stylesheet.
17 */
18(function(){
19  if (window.__faSiteNavInstalled) return;
20  window.__faSiteNavInstalled = true;
21
22  // The hamburger kicks in at 900px and below; full horizontal nav above.
23  var MOBILE_BREAKPOINT = 900;
24
25  // ---------- Hardcoded menu structure ----------
26  // Each top-level entry is an object; if it has `items`, those are sub-links
27  // that appear under a +/- toggle in the drawer.
28  // Each entry can have `items` of its own — recursive so we can mirror the
29  // desktop sub-nav's two-level "Matches > X / Odds > Y" grouping. An entry
30  // with `items` gets a +/- toggle and its children render beneath. Children
31  // may themselves have `items` (third level — used for Matches/Odds groups).
32  var NAV_STRUCTURE = [
33    {
34      "label": "Home",
35      "href": "https://www.fa-cupfinals.co.uk/"
36    },
37    {
38      "label": "FA Cup",
39      "href": "/fixtures/facup/",
40      "items": [
41        {
42          "label": "Winners",
43          "href": "/results.html"
44        },
45        {
46          "label": "Leaderboard",
47          "href": "/leaderboard.html"
48        },
49        {
50          "label": "Matches",
51          "href": "/fixtures/facup/",
52          "items": [
53            {
54              "label": "2nd Qualifying Round",
55              "href": "/fixtures/facup/2nd-qualifying-round.html"
56            },
57            {
58              "label": "3rd Qualifying Round",
59              "href": "/fixtures/facup/3rd-qualifying-round.html"
60            }
61          ]
62        },
63        {
64          "label": "Odds",
65          "href": "/odds.html",
66          "items": [
67            {
68              "label": "Outright",
69              "href": "/odds/facup/2027.html"
70            },
71            {
72              "label": "Round 1",
73              "href": "/odds/facup/1st-round.html"
74            },
75            {
76              "label": "Round 2",
77              "href": "/odds/facup/2nd-round.html"
78            }
79          ]
80        },
81        {
82          "label": "History",
83          "href": "/history/"
84        },
85        {
86          "label": "Tickets",
87          "href": "/faq/tickets.html"
88        },
89        {
90          "label": "Dates and Times",
91          "href": "/faq/"
92        }
93      ]
94    },
95    {
96      "label": "Premier League",
97      "href": "/fixtures/premierleague/",
98      "items": [
99        {
100          "label": "Matches",
101          "href": "/fixtures/premierleague/",
102          "items": [
103            {
104              "label": "Oct",
105              "href": "/fixtures/premierleague/october.html"
106            },
107            {
108              "label": "Nov",
109              "href": "/fixtures/premierleague/november.html"
110            }
111          ]
112        },
113        {
114          "label": "Table",
115          "href": "/fixtures/premierleague/table.html"
116        },
117        {
118          "label": "Teams",
119          "href": "/fixtures/teams/"
120        },
121        {
122          "label": "Odds",
123          "href": "/odds/premierleague/",
124          "items": [
125            {
126              "label": "Oct",
127              "href": "/odds/premierleague/october.html"
128            },
129            {
130              "label": "Nov",
131              "href": "/odds/premierleague/november.html"
132            },
133            {
134              "label": "Outright",
135              "href": "/odds/premierleague/outright.html"
136            },
137            {
138              "label": "Hull",
139              "href": "/odds/teams/hull-city-outright.html"
140            },
141            {
142              "label": "Brighton",
143              "href": "/odds/teams/brighton-outright.html"
144            },
145            {
146              "label": "Top Scorer",
147              "href": "/odds/premierleague/top-scorer.html"
148            },
149            {
150              "label": "Anytime Scorers",
151              "href": "/odds/premierleague/anytime-scorer.html"
152            },
153            {
154              "label": "Teams",
155              "href": "/odds/teams/"
156            }
157          ]
158        }
159      ]
160    },
161    {
162      "label": "English Leagues",
163      "href": "/fixtures/championship/",
164      "items": [
165        {
166          "label": "Championship",
167          "href": "/fixtures/championship/",
168          "items": [
169            {
170              "label": "Matches",
171              "href": "/fixtures/championship/",
172              "items": [
173                {
174                  "label": "Oct",
175                  "href": "/fixtures/championship/october.html"
176                },
177                {
178                  "label": "Teams",
179                  "href": "/fixtures/championship/teams/"
180                }
181              ]
182            },
183            {
184              "label": "Odds",
185              "href": "/odds/championship/",
186              "items": [
187                {
188                  "label": "Oct",
189                  "href": "/odds/championship/october.html"
190                },
191                {
192                  "label": "Outright",
193                  "href": "/odds/championship/outright.html"
194                },
195                {
196                  "label": "Top Scorer",
197                  "href": "/odds/championship/top-scorer.html"
198                },
199                {
200                  "label": "Anytime Scorers",
201                  "href": "/odds/championship/anytime-scorer.html"
202                },
203                {
204                  "label": "Teams",
205                  "href": "/odds/championship/teams/"
206                }
207              ]
208            }
209          ]
210        },
211        {
212          "label": "Division 1",
213          "href": "/fixtures/division-1/",
214          "items": [
215            {
216              "label": "Matches",
217              "href": "/fixtures/division-1/",
218              "items": [
219                {
220                  "label": "Oct",
221                  "href": "/fixtures/division-1/october.html"
222                },
223                {
224                  "label": "Teams",
225                  "href": "/fixtures/division-1/teams/"
226                }
227              ]
228            },
229            {
230              "label": "Odds",
231              "href": "/odds/division-1/",
232              "items": [
233                {
234                  "label": "Oct",
235                  "href": "/odds/division-1/october.html"
236                },
237                {
238                  "label": "Outright",
239                  "href": "/odds/division-1/outright.html"
240                },
241                {
242                  "label": "Top Scorer",
243                  "href": "/odds/division-1/top-scorer.html"
244                },
245                {
246                  "label": "Anytime Scorers",
247                  "href": "/odds/division-1/anytime-scorer.html"
248                },
249                {
250                  "label": "Teams",
251                  "href": "/odds/division-1/teams/"
252                }
253              ]
254            }
255          ]
256        },
257        {
258          "label": "Division 2",
259          "href": "/fixtures/division-2/",
260          "items": [
261            {
262              "label": "Matches",
263              "href": "/fixtures/division-2/",
264              "items": [
265                {
266                  "label": "Oct",
267                  "href": "/fixtures/division-2/october.html"
268                },
269                {
270                  "label": "Teams",
271                  "href": "/fixtures/division-2/teams/"
272                }
273              ]
274            },
275            {
276              "label": "Odds",
277              "href": "/odds/division-2/",
278              "items": [
279                {
280                  "label": "Oct",
281                  "href": "/odds/division-2/october.html"
282                },
283                {
284                  "label": "Outright",
285                  "href": "/odds/division-2/outright.html"
286                },
287                {
288                  "label": "Top Scorer",
289                  "href": "/odds/division-2/top-scorer.html"
290                },
291                {
292                  "label": "Anytime Scorers",
293                  "href": "/odds/division-2/anytime-scorer.html"
294                },
295                {
296                  "label": "Teams",
297                  "href": "/odds/division-2/teams/"
298                }
299              ]
300            }
301          ]
302        }
303      ]
304    },
305    {
306      "label": "More Comps",
307      "href": "/fixtures/worldcup/",
308      "items": [
309        {
310          "label": "Euro 2028",
311          "href": "/fixtures/euro-2028/",
312          "items": [
313            {
314              "label": "Matches",
315              "href": "/fixtures/euro-2028/"
316            },
317            {
318              "label": "Odds",
319              "href": "/odds/euro-2028/"
320            }
321          ]
322        },
323        {
324          "label": "Champions League",
325          "href": "/fixtures/championsleague/",
326          "items": [
327            {
328              "label": "Matches",
329              "href": "/fixtures/championsleague/",
330              "items": [
331                {
332                  "label": "League Phase",
333                  "href": "/fixtures/championsleague/league-phase.html"
334                },
335                {
336                  "label": "Final",
337                  "href": "/fixtures/championsleague/final.html"
338                }
339              ]
340            },
341            {
342              "label": "Odds",
343              "href": "/odds/championsleague/",
344              "items": [
345                {
346                  "label": "League Phase",
347                  "href": "/odds/championsleague/league-phase.html"
348                },
349                {
350                  "label": "Outright",
351                  "href": "/odds/championsleague/outright.html"
352                },
353                {
354                  "label": "Top Scorer",
355                  "href": "/odds/championsleague/top-scorer.html"
356                }
357              ]
358            }
359          ]
360        },
361        {
362          "label": "World Cup",
363          "href": "/fixtures/worldcup/",
364          "items": [
365            {
366              "label": "Matches",
367              "href": "/fixtures/worldcup/",
368              "items": [
369                {
370                  "label": "W1",
371                  "href": "/fixtures/worldcup/week-1.html"
372                },
373                {
374                  "label": "W2",
375                  "href": "/fixtures/worldcup/week-2.html"
376                },
377                {
378                  "label": "W3",
379                  "href": "/fixtures/worldcup/week-3.html"
380                },
381                {
382                  "label": "Groups",
383                  "href": "/fixtures/worldcup/groups.html"
384                }
385              ]
386            },
387            {
388              "label": "Odds",
389              "href": "/odds/worldcup/",
390              "items": [
391                {
392                  "label": "Outright",
393                  "href": "/odds/worldcup/outright.html"
394                },
395                {
396                  "label": "Top Scorer",
397                  "href": "/odds/worldcup/top-scorer.html"
398                },
399                {
400                  "label": "Teams",
401                  "href": "/odds/teams/worldcup.html"
402                }
403              ]
404            },
405            {
406              "label": "FAQ",
407              "href": "/faq/worldcup/"
408            }
409          ]
410        },
411        {
412          "label": "La Liga - Odds",
413          "href": "/odds/la-liga/",
414          "items": [
415            {
416              "label": "Outright",
417              "href": "/odds/la-liga/outright.html"
418            },
419            {
420              "label": "Top Scorer",
421              "href": "/odds/la-liga/top-scorer.html"
422            }
423          ]
424        },
425        {
426          "label": "Serie A - Odds",
427          "href": "/odds/serie-a/",
428          "items": [
429            {
430              "label": "Outright",
431              "href": "/odds/serie-a/outright.html"
432            },
433            {
434              "label": "Top Scorer",
435              "href": "/odds/serie-a/top-scorer.html"
436            }
437          ]
438        },
439        {
440          "label": "Bundesliga - Odds",
441          "href": "/odds/bundesliga/",
442          "items": [
443            {
444              "label": "Outright",
445              "href": "/odds/bundesliga/outright.html"
446            },
447            {
448              "label": "Top Scorer",
449              "href": "/odds/bundesliga/top-scorer.html"
450            }
451          ]
452        },
453        {
454          "label": "Ligue 1 - Odds",
455          "href": "/odds/ligue-1/",
456          "items": [
457            {
458              "label": "Outright",
459              "href": "/odds/ligue-1/outright.html"
460            },
461            {
462              "label": "Top Scorer",
463              "href": "/odds/ligue-1/top-scorer.html"
464            }
465          ]
466        }
467      ]
468    },
469    {
470      "label": "Tips",
471      "href": "/betting/"
472    },
473    {
474      "label": "Games",
475      "href": "/games/"
476    },
477    {
478      "label": "Contact Us",
479      "href": "/contact/"
480    }
481  ];
482  var DESKTOP_STRUCTURE = [
483    {
484      "label": "Home",
485      "href": "https://www.fa-cupfinals.co.uk/",
486      "class": "home"
487    },
488    {
489      "label": "FA Cup",
490      "href": "/fixtures/facup/",
491      "class": "facupoddslink",
492      "items": [
493        {
494          "label": "Winners",
495          "href": "/results.html"
496        },
497        {
498          "label": "Leaderboard",
499          "href": "/leaderboard.html"
500        },
501        {
502          "label": "Matches",
503          "href": "/fixtures/facup/",
504          "items": [
505            {
506              "label": "2nd Qualifying Round",
507              "href": "/fixtures/facup/2nd-qualifying-round.html"
508            },
509            {
510              "label": "3rd Qualifying Round",
511              "href": "/fixtures/facup/3rd-qualifying-round.html"
512            }
513          ]
514        },
515        {
516          "label": "Odds",
517          "href": "/odds.html",
518          "items": [
519            {
520              "label": "Outright",
521              "href": "/odds.html"
522            },
523            {
524              "label": "Hull",
525              "href": "/odds/teams/hull-facup.html",
526              "class": "nav-subitem"
527            },
528            {
529              "label": "Brighton",
530              "href": "/odds/teams/brighton-facup.html",
531              "class": "nav-subitem"
532            },
533            {
534              "label": "Leeds",
535              "href": "/odds/teams/leeds-facup.html",
536              "class": "nav-subitem"
537            },
538            {
539              "label": "Round 1",
540              "href": "/odds/facup/1st-round.html"
541            },
542            {
543              "label": "Round 2",
544              "href": "/odds/facup/2nd-round.html"
545            }
546          ]
547        },
548        {
549          "label": "History",
550          "href": "/history/"
551        },
552        {
553          "label": "Tickets",
554          "href": "/faq/tickets.html"
555        },
556        {
557          "label": "Dates & Times",
558          "href": "/faq/"
559        }
560      ]
561    },
562    {
563      "label": "Premier League",
564      "href": "/fixtures/premierleague/",
565      "class": "premierleaguelink",
566      "items": [
567        {
568          "label": "Matches",
569          "href": "/fixtures/premierleague/",
570          "items": [
571            {
572              "label": "Oct",
573              "href": "/fixtures/premierleague/october.html"
574            },
575            {
576              "label": "Nov",
577              "href": "/fixtures/premierleague/november.html"
578            }
579          ]
580        },
581        {
582          "label": "Table",
583          "href": "/fixtures/premierleague/table.html"
584        },
585        {
586          "label": "Teams",
587          "href": "/fixtures/teams/"
588        },
589        {
590          "label": "Odds",
591          "href": "/odds/premierl
591eague/",
592          "items": [
593            {
594              "label": "Oct",
595              "href": "/odds/premierleague/october.html"
596            },
597            {
598              "label": "Nov",
599              "href": "/odds/premierleague/november.html"
600            },
601            {
602              "label": "Outright",
603              "href": "/odds/premierleague/outright.html"
604            },
605            {
606              "label": "Hull",
607              "href": "/odds/teams/hull-city-outright.html",
608              "class": "nav-subitem"
609            },
610            {
611              "label": "Brighton",
612              "href": "/odds/teams/brighton-outright.html",
613              "class": "nav-subitem"
614            },
615            {
616              "label": "Leeds",
617              "href": "/odds/teams/leeds-outright.html",
618              "class": "nav-subitem"
619            },
620            {
621              "label": "Top Scorer",
622              "href": "/odds/premierleague/top-scorer.html"
623            },
624            {
625              "label": "Anytime Scorers",
626              "href": "/odds/premierleague/anytime-scorer.html"
627            },
628            {
629              "label": "Teams",
630              "href": "/odds/teams/"
631            }
632          ]
633        }
634      ]
635    },
636    {
637      "label": "English Leagues",
638      "href": "/fixtures/championship/",
639      "class": "championshiplink",
640      "items": [
641        {
642          "label": "Championship",
643          "href": "/fixtures/championship/",
644          "items": [
645            {
646              "label": "Matches",
647              "href": "/fixtures/championship/",
648              "items": [
649                {
650                  "label": "Oct",
651                  "href": "/fixtures/championship/october.html"
652                },
653                {
654                  "label": "Teams",
655                  "href": "/fixtures/championship/teams/"
656                }
657              ]
658            },
659            {
660              "label": "Odds",
661              "href": "/odds/championship/",
662              "items": [
663                {
664                  "label": "Oct",
665                  "href": "/odds/championship/october.html"
666                },
667                {
668                  "label": "Outright",
669                  "href": "/odds/championship/outright.html"
670                },
671                {
672                  "label": "Top Scorer",
673                  "href": "/odds/championship/top-scorer.html"
674                },
675                {
676                  "label": "Anytime Scorers",
677                  "href": "/odds/championship/anytime-scorer.html"
678                },
679                {
680                  "label": "Teams",
681                  "href": "/odds/championship/teams/"
682                }
683              ]
684            }
685          ]
686        },
687        {
688          "label": "Division 1",
689          "href": "/fixtures/division-1/",
690          "items": [
691            {
692              "label": "Matches",
693              "href": "/fixtures/division-1/",
694              "items": [
695                {
696                  "label": "Oct",
697                  "href": "/fixtures/division-1/october.html"
698                },
699                {
700                  "label": "Teams",
701                  "href": "/fixtures/division-1/teams/"
702                }
703              ]
704            },
705            {
706              "label": "Odds",
707              "href": "/odds/division-1/",
708              "items": [
709                {
710                  "label": "Oct",
711                  "href": "/odds/division-1/october.html"
712                },
713                {
714                  "label": "Outright",
715                  "href": "/odds/division-1/outright.html"
716                },
717                {
718                  "label": "Top Scorer",
719                  "href": "/odds/division-1/top-scorer.html"
720                },
721                {
722                  "label": "Anytime Scorers",
723                  "href": "/odds/division-1/anytime-scorer.html"
724                },
725                {
726                  "label": "Teams",
727                  "href": "/odds/division-1/teams/"
728                }
729              ]
730            }
731          ]
732        },
733        {
734          "label": "Division 2",
735          "href": "/fixtures/division-2/",
736          "items": [
737            {
738              "label": "Matches",
739              "href": "/fixtures/division-2/",
740              "items": [
741                {
742                  "label": "Oct",
743                  "href": "/fixtures/division-2/october.html"
744                },
745                {
746                  "label": "Teams",
747                  "href": "/fixtures/division-2/teams/"
748                }
749              ]
750            },
751            {
752              "label": "Odds",
753              "href": "/odds/division-2/",
754              "items": [
755                {
756                  "label": "Oct",
757                  "href": "/odds/division-2/october.html"
758                },
759                {
760                  "label": "Outright",
761                  "href": "/odds/division-2/outright.html"
762                },
763                {
764                  "label": "Top Scorer",
765                  "href": "/odds/division-2/top-scorer.html"
766                },
767                {
768                  "label": "Anytime Scorers",
769                  "href": "/odds/division-2/anytime-scorer.html"
770                },
771                {
772                  "label": "Teams",
773                  "href": "/odds/division-2/teams/"
774                }
775              ]
776            }
777          ]
778        }
779      ]
780    },
781    {
782      "label": "More Comps",
783      "href": "/fixtures/worldcup/",
784      "class": "worldcuplink",
785      "items": [
786        {
787          "label": "Euro 2028",
788          "href": "/fixtures/euro-2028/",
789          "items": [
790            {
791              "label": "Matches",
792              "href": "/fixtures/euro-2028/"
793            },
794            {
795              "label": "Odds",
796              "href": "/odds/euro-2028/"
797            }
798          ]
799        },
800        {
801          "label": "Champions League",
802          "href": "/fixtures/championsleague/",
803          "class": "championsleaguelink",
804          "items": [
805            {
806              "label": "Matches",
807              "href": "/fixtures/championsleague/",
808              "items": [
809                {
810                  "label": "League Phase",
811                  "href": "/fixtures/championsleague/league-phase.html"
812                },
813                {
814                  "label": "Final",
815                  "href": "/fixtures/championsleague/final.html"
816                }
817              ]
818            },
819            {
820              "label": "Odds",
821              "href": "/odds/championsleague/",
822              "items": [
823                {
824                  "label": "League Phase",
825                  "href": "/odds/championsleague/league-phase.html"
826                },
827                {
828                  "label": "Outright",
829                  "href": "/odds/championsleague/outright.html"
830                },
831                {
832                  "label": "Top Scorer",
833                  "href": "/odds/championsleague/top-scorer.html"
834                }
835              ]
836            }
837          ]
838        },
839        {
840          "label": "World Cup",
841          "href": "/fixtures/worldcup/",
842          "items": [
843            {
844              "label": "Matches",
845              "href": "/fixtures/worldcup/",
846              "items": [
847                {
848                  "label": "W1",
849                  "href": "/fixtures/worldcup/week-1.html"
850                },
851                {
852                  "label": "W2",
853                  "href": "/fixtures/worldcup/week-2.html"
854                },
855                {
856                  "label": "W3",
857                  "href": "/fixtures/worldcup/week-3.html"
858                },
859                {
860                  "label": "Groups",
861                  "href": "/fixtures/worldcup/groups.html"
862                }
863              ]
864            },
865            {
866              "label": "Odds",
867              "href": "/odds/worldcup/",
868              "items": [
869                {
870                  "label": "Outright",
871                  "href": "/odds/worldcup/outright.html"
872                },
873                {
874                  "label": "Top Scorer",
875                  "href": "/odds/worldcup/top-scorer.html"
876                },
877                {
878                  "label": "Teams",
879                  "href": "/odds/teams/worldcup.html"
880                }
881              ]
882            },
883            {
884              "label": "Dates and Times",
885              "href": "/faq/worldcup/"
886            }
887          ]
888        },
889        {
890          "label": "La Liga - Odds",
891          "href": "/odds/la-liga/",
892          "items": [
893            {
894              "label": "Outright",
895              "href": "/odds/la-liga/outright.html"
896            },
897            {
898              "label": "Top Scorer",
899              "href": "/odds/la-liga/top-scorer.html"
900            }
901          ]
902        },
903        {
904          "label": "Serie A - Odds",
905          "href": "/odds/serie-a/",
906          "items": [
907            {
908              "label": "Outright",
909              "href": "/odds/serie-a/outright.html"
910            },
911            {
912              "label": "Top Scorer",
913              "href": "/odds/serie-a/top-scorer.html"
914            }
915          ]
916        },
917        {
918          "label": "Bundesliga - Odds",
919          "href": "/odds/bundesliga/",
920          "items": [
921            {
922              "label": "Outright",
923              "href": "/odds/bundesliga/outright.html"
924            },
925            {
926              "label": "Top Scorer",
927              "href": "/odds/bundesliga/top-scorer.html"
928            }
929          ]
930        },
931        {
932          "label": "Ligue 1 - Odds",
933          "href": "/odds/ligue-1/",
934          "items": [
935            {
936              "label": "Outright",
937              "href": "/odds/ligue-1/outright.html"
938            },
939            {
940              "label": "Top Scorer",
941              "href": "/odds/ligue-1/top-scorer.html"
942            }
943          ]
944        }
945      ]
946    },
947    {
948      "label": "Tips",
949      "href": "/betting/",
950      "class": "bettinglink"
951    },
952    {
953      "label": "Games",
954      "href": "/games/",
955      "class": "gameslink"
956    },
957    {
958      "label": "Contact Us",
959      "href": "/contact/",
960      "class": "postid-contact"
961    }
962  ];
963  var EXCLUSIONS = [];
964
965  // ---------- PWA-only nav (focused horse-racing index) ----------
966  // When the visitor has installed the app to their home screen and
967  // launches it from the shortcut, body.pwa-mode is added by
968  // /betting/templegate-app/pwa.js. We want the hamburger drawer in
969  // that mode to show a focused horse-racing index instead of the
970  // whole-site NAV_STRUCTURE — football links etc. aren't relevant
971  // when the installed app is "Racing Tips Tracker".
972  var PWA_NAV_STRUCTURE = [
973    { label: "Today's Tips",
974      href: '/betting/racing-tips.html' },
975    { label: "Yesterday's Tips vs Results",
976      href: '/betting/racing-tips-yesterday.html' },
977    { label: 'Tipster Tracker', href: '/betting/racing-tips.html', items: [
978      { label: 'Templegate',  href: '/betting/templegate.html' },
979      { label: 'Racing Post', href: '/betting/racing-tips-tipster-racingpost.html' }
980    ]},
981    { label: 'Big Event Tips', href: '/betting/racing-tips.html', items: [
982      { label: 'Grand National',      href: '/betting/racing-tips-grand-national.html' },
983      { label: 'Cheltenham Festival', href: '/betting/racing-tips-cheltenham-festival.html' },
984      { label: 'Royal Ascot',         href: '/betting/racing-tips-royal-ascot.html' },
985      { label: 'Epsom Derby',         href: '/betting/racing-tips-the-derby.html' },
986      { label: 'King George Chase',   href: '/betting/racing-tips-king-george-stakes.html' }
987    ]}
988  ];
989
990  // ---------- Padel-section nav (mobile hamburger only) ----------
991  // When the visitor is on a padel page (body.padel-page), the football
992  // sections (FA Cup, Premier League, World Cup, Betting, More Sports)
993  // are irrelevant to the journey. The hamburger drawer focuses on the
994  // 3 padel destinations - same pattern as PWA_NAV_STRUCTURE above.
995  // Desktop horizontal nav stays untouched (still shows the whole-site
996  // football items) because site-nav.js only builds the MOBILE drawer.
997  var PADEL_NAV_STRUCTURE = [
998    { label: 'Live Padel Rankings',  href: '/padel/rankings.html' },
999    /* User asked 2026-06-01: Tournament Schedule link in hamburger after
1000       Rankings, before Padel Travel/Courts. Travel link will come back
1001       once we have more articles in that section. */
1002    { label: 'Tournament Schedule',  href: '/padel/tournaments.html' },
1003    { label: 'Padel Travel',         href: '/padel/travel.html' },
1004    { label: 'Padel Courts',         href: '/padel/courts.html' },
1005    /* User asked 2026-06-01: contact link at the bottom of the padel
1006       hamburger menu so visitors can reach /padel/contact/ from any
1007       padel page without needing the now-hidden subnav. */
1008    { label: 'Contact Us',           href: '/padel/contact/' }
1009  ];
1010
1011  // Padel-page detection - mirrors the PWA detection pattern below.
1012  // body.padel-page is hard-coded on every padel HTML page.
1013  function isPadelPage() {
1014    try {
1015      return document.body && document.body.classList.contains('padel-page');
1016    } catch (e) { return false; }
1017  }
1018
1019  // Cheap PWA detection - matches the criteria pwa.js uses to add
1020  // body.pwa-mode (display-mode standalone, iOS navigator.standalone,
1021  // ?pwa=1 cold-launch URL, or our cached _tgPWA flag). Duplicating
1022  // the check here so we don't depend on pwa.js having run first
1023  // (race condition: both scripts wire at DOMContentLoaded).
1024  function isPwaStandalone() {
1025    try {
1026      if (window._isTemplegatePWA === true) return true;
1027      if (window.navigator && window.navigator.standalone === true) return true;
1028      if (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) return true;
1029      if (window.location && window.location.search && window.location.search.indexOf('pwa=1') !== -1) return true;
1030      if (window.localStorage && window.localStorage.getItem('_tgPWA') === '1') return true;
1031    } catch (e) {}
1032    return false;
1033  }
1034
1035  // ---------- CSS lives in /css/style.css (search for "Hamburger nav")
1036  //            Nothing to inject from here - this script only builds the
1037  //            drawer markup and wires events. ----------
1038
1039  // ---------- Build the drawer from NAV_STRUCTURE ----------
1040  function init() {
1041    var header = document.querySelector('header.site-header');
1042    if (!header) return;
1043    // Rebuild even if a previous version of the script ran - the new schema
1044    // (hardcoded NAV_STRUCTURE) supersedes any DOM-derived attempt.
1045    var existingToggle = header.querySelector('.fa-nav-toggle');
1046    var existingDrawer = header.querySelector('.fa-nav-drawer');
1047    if (existingToggle) existingToggle.remove();
1048    if (existingDrawer) existingDrawer.remove();
1049
1050    var toggle = document.createElement('button');
1051    toggle.type = 'button';
1052    toggle.className = 'fa-nav-toggle';
1053    toggle.setAttribute('aria-label', 'Open navigation menu');
1054    toggle.setAttribute('aria-expanded', 'false');
1055    toggle.setAttribute('aria-controls', 'fa-nav-drawer');
1056    toggle.innerHTML = '<span></span><span></span><span></span>';
1057
1058    var drawer = document.createElement('ul');
1059    drawer.className = 'fa-nav-drawer';
1060    drawer.id = 'fa-nav-drawer';
1061
1062    var currentPath = location.pathname;
1063
1064    // Recursive builder so any entry with `items` can carry its own +/- and
1065    // a nested sub-list. Depth controls which class set we attach so the CSS
1066    // can style level-1 (parent) vs level-2 (nested under a sub-parent).
1067    function buildEntry(entry, depth) {
1068      var li = document.createElement('li');
1069      var a = document.createElement('a');
1070      a.href = entry.href;
1071      a.textContent = entry.label;
1072      li.appendChild(a);
1073      var _ecls = entry['class'] || entry.cls; if (_ecls) li.className = (li.className ? li.className + ' ' : '') + _ecls;
1074      if (depth === 0 && sectionMatches(currentPath, entry)) {
1075        li.classList.add('fa-nav-current');
1076      }
1077      if (Array.isArray(entry.items) && entry.items.length) {
1078        li.classList.add('fa-nav-has-sub');
1079        var subToggle = document.createElement('button');
1080        subToggle.type = 'button';
1081        subToggle.className = 'fa-nav-sub-toggle';
1082        subToggle.setAttribute('aria-label', 'Toggle ' + entry.label + ' submenu');
1083        subToggle.setAttribute('aria-expanded', 'false');
1084        subToggle.innerHTML = '<span class="fa-nav-sub-icon" aria-hidden="true"></span>';
1085        li.appendChild(subToggle);
1086        var subUl = document.createElement('ul');
1087        subUl.className = 'fa-nav-sub' + (depth >= 1 ? ' fa-nav-sub-deep' : '');
1088        entry.items.forEach(function(child) {
1089          subUl.appendChild(buildEntry(child, depth + 1));
1090        });
1091        li.appendChild(subUl);
1092      }
1093      return li;
1094    }
1095    // Pick the right nav for context:
1096    //   Padel page    -> padel links only (PADEL_NAV_STRUCTURE)
1097    //                    Wins over PWA detection because the padel PWA
1098    //                    runs in standalone too and would otherwise
1099    //                    inherit the horse-racing PWA_NAV_STRUCTURE.
1100    //   PWA mode      -> focused horse-racing index (PWA_NAV_STRUCTURE)
1101    //   anything else -> whole-site default (NAV_STRUCTURE)
1102    var navData = isPadelPage()
1103      ? PADEL_NAV_STRUCTURE
1104      : (isPwaStandalone() ? PWA_NAV_STRUCTURE : NAV_STRUCTURE);
1105    navData.forEach(function(entry) {
1106      if (_navExcluded(entry.label)) return;
1107      drawer.appendChild(buildEntry(entry, 0));
1108    });
1109
1110    header.appendChild(toggle);
1111    header.appendChild(drawer);
1112
1113    // ----- Open/close handlers -----
1114    function setOpen(open) {
1115      drawer.classList.toggle('open', open);
1116      toggle.classList.toggle('open', open);
1117      toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
1118      toggle.setAttribute('aria-label', open ? 'Close navigation menu' : 'Open navigation menu');
1119    }
1120    toggle.addEventListener('click', function(e){
1121      e.stopPropagation();
1122      setOpen(!drawer.classList.contains('open'));
1123    });
1124    // Close when tapping a destination link (NOT the +/- sub-toggle).
1125    drawer.addEventListener('click', function(e){
1126      if (e.target.closest('.fa-nav-sub-toggle')) return;
1127      if (e.target.closest('a')) setOpen(false);
1128    });
1129    // +/- sub-toggle: animate height from 0 -> scrollHeight (and back).
1130    // Accordion behaviour: opening a parent first triggers the collapse of
1131    // any sibling parents already open at the same level, then opens the
1132    // new one once the close animation has played out so the user sees the
1133    // transition rather than a snap-swap.
1134    var SUB_TRANSITION_MS = 220;
1135    function closeLi(targetLi){
1136      var sub = targetLi.querySelector(':scope > .fa-nav-sub');
1137      var btn = targetLi.querySelector(':scope > .fa-nav-sub-toggle');
1138      if (btn) btn.setAttribute('aria-expanded', 'false');
1139      if (!sub) { targetLi.classList.remove('open'); return; }
1140      sub.style.height = sub.scrollHeight + 'px';
1141      sub.offsetHeight; // force layout so transition has a numeric start
1142      requestAnimationFrame(function(){
1143        targetLi.classList.remove('open');
1144        sub.style.height = '0';
1145      });
1146    }
1147    function openLi(targetLi){
1148      var sub = targetLi.querySelector(':scope > .fa-nav-sub');
1149      var btn = targetLi.querySelector(':scope > .fa-nav-sub-toggle');
1150      if (btn) btn.setAttribute('aria-expanded', 'true');
1151      if (!sub) { targetLi.classList.add('open'); return; }
1152      targetLi.classList.add('open');
1153      sub.style.height = sub.scrollHeight + 'px';
1154      var onEnd = function(ev){
1155        if (ev.propertyName !== 'height') return;
1156        sub.style.height = 'auto';
1157        sub.removeEventListener('transitionend', onEnd);
1158      };
1159      sub.addEventListener('transitionend', onEnd);
1160    }
1161    drawer.addEventListener('click', function(e){
1162      var btn = e.target.closest('.fa-nav-sub-toggle');
1163      if (!btn) return;
1164      e.preventDefault();
1165      e.stopPropagation();
1166      var li = btn.closest('.fa-nav-has-sub');
1167      if (!li) return;
1168      var willOpen = !li.classList.contains('open');
1169      if (!willOpen) {
1170        closeLi(li);
1171        return;
1172      }
1173      // Close any open siblings at this level first, then open the new one
1174      // after the collapse transition has had time to play.
1175      var parentList = li.parentElement;
1176      var siblings = parentList ? Array.from(parentList.children).filter(function(c){
1177        return c !== li && c.classList && c.classList.contains('fa-nav-has-sub') && c.classList.contains('open');
1178      }) : [];
1179      siblings.forEach(closeLi);
1180      if (siblings.length) {
1181        setTimeout(function(){ openLi(li); }, SUB_TRANSITION_MS);
1182      } else {
1183        openLi(li);
1184      }
1185    });
1186    document.addEventListener('keydown', function(e){
1187      if (e.key === 'Escape' && drawer.classList.contains('open')) setOpen(false);
1188    });
1189    document.addEventListener('click', function(e){
1190      if (!drawer.classList.contains('open')) return;
1191      if (e.target.closest('.site-header')) return;
1192      setOpen(false);
1193    });
1194    // Close drawer if viewport grows past the breakpoint (CSS hides it but
1195    // we want the state clean for when the user resizes back).
1196    window.addEventListener('resize', function(){
1197      if (window.innerWidth > MOBILE_BREAKPOINT && drawer.classList.contains('open')) {
1198        setOpen(false);
1199      }
1200    });
1201  }
1202
1203  // ---------- Desktop horizontal nav: ensure a Champions League link ----------
1204  // The desktop nav (>900px) is hardcoded as <ul> markup inside each page's
1205  // header, NOT built by this script. Most pages were authored before the
1206  // Champions League section existed, so their .newmenu list is missing it.
1207  // Rather than edit dozens of templates + static pages, inject the link
1208  // here so it appears site-wide. The Champions League section pages already
1209  // ship a hardcoded `.championsleaguelink` <li>, so the guard below skips
1210  // them to avoid a duplicate. Inserted right after Premier League to match
1211  // the order the CL section pages already use (FA Cup, PL, CL, World Cup).
1212  function ensureChampionsLeagueDesktopLink() {
1213    var ul = document.querySelector('.newmenu > ul');
1214    if (!ul) return;
1215    if (ul.querySelector('.championsleaguelink')) return; // already present
1216    var li = document.createElement('li');
1217    li.className = 'championsleaguelink';
1218    var a = document.createElement('a');
1219    a.href = '/fixtures/championsleague/';
1220    a.textContent = 'Champions League';
1221    li.appendChild(a);
1222    var pl = ul.querySelector('.premierleaguelink');
1223    if (pl) {
1224      ul.insertBefore(li, pl.nextSibling);
1225    } else {
1226      var wc = ul.querySelector('.worldcuplink');
1227      if (wc) ul.insertBefore(li, wc);
1228      else ul.appendChild(li);
1229    }
1230  }
1231
1232  // La Liga desktop link, published site-wide AFTER Champions League. Section pages carry
1233  // body.laligapage; la-liga.css uses it to light this link and cancel the inherited
1234  // Premier League highlight (prempage stays on the body for styling).
1235  function ensureLaLigaDesktopLink() {
1236    var ul = document.querySelector('.newmenu > ul');
1237    if (!ul) return;
1238    if (ul.querySelector('.laligalink')) return; // already present
1239    var li = document.createElement('li');
1240    li.className = 'laligalink';
1241    var a = document.createElement('a');
1242    a.href = '/fixtures/la-liga/';
1243    a.textContent = 'La Liga';
1244    li.appendChild(a);
1245    var cl = ul.querySelector('.championsleaguelink');
1246    if (cl) {
1247      ul.insertBefore(li, cl.nextSibling);
1248    } else {
1249      var wc = ul.querySelector('.worldcuplink');
1250      if (wc) ul.insertBefore(li, wc);
1251      else ul.appendChild(li);
1252    }
1253  }
1254
1255  // Bundesliga desktop link, inserted after La Liga.
1256  function ensureBundesligaDesktopLink() {
1257    var ul = document.querySelector('.newmenu > ul');
1258    if (!ul) return;
1259    if (ul.querySelector('.bundesligalink')) return;
1260    var li = document.createElement('li');
1261    li.className = 'bundesligalink';
1262    var a = document.createElement('a');
1263    a.href = '/fixtures/bundesliga/';
1264    a.textContent = 'Bundesliga';
1265    li.appendChild(a);
1266    var la = ul.querySelector('.laligalink');
1267    if (la) { ul.insertBefore(li, la.nextSibling); }
1268    else {
1269      var wc = ul.querySelector('.worldcuplink');
1270      if (wc) ul.insertBefore(li, wc); else ul.appendChild(li);
1271    }
1272  }
1273
1274  // Inject the Championship link into the desktop .newmenu site-wide (most
1275  // templates predate the section). Inserted right after Premier League so the
1276  // order reads FA Cup, Premier League, Championship, Champions League, World Cup.
1277  // Runs AFTER ensureChampionsLeagueDesktopLink in boot() so it lands between PL
1278  // and the just-inserted Champions League link.
1279  // Style the English Leagues desktop dropdown: lift it ABOVE the subnav (both were
1280  // positioned with z:auto so the later subnav painted over it), and give it the FA Cup
1281  // draw-table look - red border + alternating row colours, red hover. Injected once.
1282  function ensureDesktopDropdownStyle() {
1283    if (document.getElementById('englishleagues-dd-style')) return;
1284    var st = document.createElement('style');
1285    st.id = 'englishleagues-dd-style';
1286    st.textContent =
1287      '.newmenu .championshiplink{z-index:9999 !important;}' +
1288      '.newmenu .championshiplink > ul{z-index:9999 !important;border:2px solid #4d4a4a !important;border-radius:0 0 6px 6px !important;background:#fff !important;
1288min-width:200px !important;box-shadow:0 4px 12px rgba(0,0,0,.18) !important;padding:0 !important;overflow:hidden !important;}' +
1289      '.newmenu .championshiplink > ul > li > a{padding:9px 14px !important;font-size:10pt !important;color:#1b1b1b !important;border-bottom:1px solid #f0e2da !important;}' +
1290      '.newmenu .championshiplink > ul > li:last-child > a{border-bottom:0 !important;}' +
1291      '.newmenu .championshiplink > ul > li:nth-child(odd) > a{background:#fff !important;}' +
1292      '.newmenu .championshiplink > ul > li:nth-child(even) > a{background:#fbf7f1 !important;}' +
1293      '.newmenu .championshiplink > ul > li > a:hover{background:#4d4a4a !important;color:#fff !important;}' +
1294      '.newmenu .championshiplink > ul > li.dd-active > a{background:#4d4a4a !important;color:#fff !important;}';
1295    (document.head || document.documentElement).appendChild(st);
1296  }
1297
1298  function ensureChampionshipDesktopLink() {
1299    var ul = document.querySelector('.newmenu > ul');
1300    if (!ul) return;
1301    ensureDesktopDropdownStyle();
1302    if (ul.querySelector('.championshiplink')) return; // already present
1303    var li = document.createElement('li');
1304    li.className = 'championshiplink';
1305    li.style.position = 'relative'; // anchor the absolute dropdown to this li
1306    var a = document.createElement('a');
1307    a.href = '/fixtures/championship/';
1308    a.textContent = 'English Leagues';
1309    li.appendChild(a);
1310    // Desktop hover dropdown (uses the existing .newmenu ul ul CSS): the three
1311    // English league sections - Championship, Division 1, Division 2.
1312    var sub = document.createElement('ul');
1313    [['Championship', '/fixtures/championship/'],
1314     ['Division 1',   '/fixtures/division-1/'],
1315     ['Division 2',   '/fixtures/division-2/']].forEach(function (it) {
1316      var sli = document.createElement('li');
1317      var sa = document.createElement('a');
1318      sa.href = it[1];
1319      sa.textContent = it[0];
1320      var ddKey = it[1].split('/').filter(Boolean)[1]; // championship / division-1 / division-2
1321      if (ddKey && location.pathname.indexOf('/' + ddKey + '/') !== -1) sli.className = 'dd-active';
1322      sli.appendChild(sa);
1323      sub.appendChild(sli);
1324    });
1325    li.appendChild(sub);
1326    var pl = ul.querySelector('.premierleaguelink');
1327    if (pl) { ul.insertBefore(li, pl.nextSibling); }
1328    else { ul.appendChild(li); }
1329  }
1330
1331  function sectionMatches(path, entry) {
1332    if (!entry || !entry.href) return false;
1333    var href = entry.href;
1334    // Trim domain prefix if present (Home link).
1335    href = href.replace(/^https?:\/\/[^/]+/, '');
1336    if (!href || href === '/') return path === '/' || path === '/index.html';
1337    // For section entries, match the leading path segment.
1338    var key = href.split('/').filter(Boolean)[0]; // 'fixtures' / 'odds' / 'betting' / 'sports' / 'contact'
1339    if (!key) return false;
1340    // The "FA Cup" / "Premier League" / "World Cup" entries point at
1341    // /fixtures/{league}/ but the section also lives at /odds/{league}/.
1342    // Match both by also checking the second path segment.
1343    var subKey = href.split('/').filter(Boolean)[1] || '';
1344    // "English Leagues" (Championship) also owns Division 1 & 2, which live under
1345    // different path segments (/fixtures/division-1/, /odds/division-2/, etc.).
1346    if (subKey === 'championship' && (path.indexOf('/division-1/') !== -1 || path.indexOf('/division-2/') !== -1)) return true;
1347    if (subKey) {
1348      return path.indexOf('/' + subKey + '/') !== -1;
1349    }
1350    return path.indexOf('/' + key + '/') === 0 || path.indexOf('/' + key) === 0;
1351  }
1352
1353  // Desktop: turn the hardcoded 'World Cup' link into a 'More Comps' parent with a
1354  // hover dropdown (World Cup + Euro 2028). The .worldcuplink class is kept so the
1355  // existing worldcuppage highlight still lights it; euro2028page highlight added below.
1356  function restructureMoreComps() {
1357    var ul = document.querySelector('.newmenu > ul');
1358    if (!ul) return;
1359    var wc = ul.querySelector('.worldcuplink');
1360    if (!wc) {
1361      // older nav markup (home/racing/betting/match reports): the World Cup <li> has no
1362      // .worldcuplink class - find it by href or text and tag it so the rename + CSS apply.
1363      var _lis = ul.children;
1364      for (var _i = 0; _i < _lis.length; _i++) {
1365        var _a = _lis[_i].querySelector('a');
1366        var _h = _a ? (_a.getAttribute('href') || '') : '';
1367        if (_a && (_h.indexOf('/worldcup/') !== -1 || _a.textContent.trim() === 'World Cup')) {
1368          wc = _lis[_i]; wc.classList.add('worldcuplink'); break;
1369        }
1370      }
1371    }
1372    if (!wc || wc.getAttribute('data-morecomps')) return;
1373    wc.setAttribute('data-morecomps', '1');
1374    wc.style.position = 'relative';
1375    var a = wc.querySelector('a');
1376    if (a) a.textContent = 'More Comps';
1377    var sub = document.createElement('ul');
1378    [['World Cup', '/fixtures/worldcup/', 'worldcup'],
1379     ['Euro 2028', '/fixtures/euro-2028/', 'euro-2028'],
1380     ['Serie A', '/fixtures/serie-a/', 'serie-a'],
1381     ['French Ligue 1', '/fixtures/ligue-1/', 'ligue-1']].forEac
1381h(function (it) {
1382      var sli = document.createElement('li');
1383      var sa = document.createElement('a');
1384      sa.href = it[1]; sa.textContent = it[0];
1385      if (location.pathname.indexOf('/' + it[2] + '/') !== -1) sli.className = 'dd-active';
1386      sli.appendChild(sa); sub.appendChild(sli);
1387    });
1388    wc.appendChild(sub);
1389    if (!document.getElementById('morecomps-style')) {
1390      var st = document.createElement('style'); st.id = 'morecomps-style';
1391      st.textContent =
1392        '.newmenu .worldcuplink{position:relative;}' +
1393        '.newmenu .worldcuplink > ul{display:none;position:absolute;left:0;top:100%;margin:0;padding:0;list-style:none;z-index:9999;border:2px solid #4d4a4a;border-radius:0 0 6px 6px;background:#fff;min-width:180px;box-shadow:0 4px 12px rgba(0,0,0,.18);overflow:hidden;}' +
1394        '.newmenu .worldcuplink:hover > ul{display:block;}' +
1395        '.newmenu .worldcuplink > ul > li{float:none;margin:0;}' +
1396        '.newmenu .worldcuplink > ul > li > a{display:block;padding:9px 14px;font-size:10pt;color:#1b1b1b;border-bottom:1px solid #f0e2da;white-space:nowrap;}' +
1397        '.newmenu .worldcuplink > ul > li:last-child > a{border-bottom:0;}' +
1398        '.newmenu .worldcuplink > ul > li:nth-child(even) > a{background:#fbf7f1;}' +
1399        '.newmenu .worldcuplink > ul > li > a:hover{background:#4d4a4a;color:#fff;}' +
1400        '.newmenu .worldcuplink > ul > li.dd-active > a{background:#4d4a4a;color:#fff;}' +
1401        'body.euro2028page .newmenu > ul > li.worldcuplink > a{background-color:#4d4a4a !important;}' +
1402        'body.serieapage .newmenu > ul > li.worldcuplink > a{background-color:#4d4a4a !important;}' +
1403        'body.ligue1page .newmenu > ul > li.worldcuplink > a{background-color:#4d4a4a !important;}' +
1404        'body.laligapage .newmenu > ul > li.worldcuplink > a{background-color:#4d4a4a !important;}' +
1405        'body.bundesligapage .newmenu > ul > li.worldcuplink > a{background-color:#4d4a4a !important;}' +
1406        'body.championsleaguepage .newmenu > ul > li.worldcuplink > a{background-color:#4d4a4a !important;}';
1407      document.head.appendChild(st);
1408    }
1409  }
1410
1411  // Build desktop hover-dropdowns from NAV_STRUCTURE (the admin-edited nav tree) so grouped items like
1412  // "More Comps" / "English Leagues" show their child leagues on desktop, matching the mobile hamburger.
1413  // Replaces the old hardcoded restructureMoreComps + ensure*DesktopLink injections. (user 2026-08-23)
1414  // Is this top-level nav label excluded on the current page? (data-driven, EXCLUSIONS from admin) (user 2026-08-23)
1415  function _navExcluded(label) {
1416    var p = location.pathname, i, ex;
1417    for (i = 0; i < (EXCLUSIONS || []).length; i++) {
1418      ex = EXCLUSIONS[i];
1419      if ((ex.labels || []).indexOf(label) === -1) continue;
1420      var hit = false;
1421      try { if (ex.pathRegex && new RegExp(ex.pathRegex).test(p)) hit = true; } catch (e) {}
1422      if (ex.prefix && p.indexOf(ex.prefix) === 0) hit = true;
1423      if (ex.path && p === ex.path) hit = true;
1424      if (ex.bodyClass && document.body && document.body.classList.contains(ex.bodyClass)) hit = true;
1425      if (hit) return true;
1426    }
1427    return false;
1428  }
1429
1430  function _ddAct(u) { if (!u || u === '/') return false; var p = location.pathname; if (p === u) return true; if (u.charAt(u.length - 1) === '/' && (p === u || p === u + 'index.html')) return true; return false; }
1431  // current page competition = the URL section (/odds/<comp>/ or /fixtures/<comp>/).
1432  // Keyed on the URL, NOT the body class: some cloned pages carry more than one comp body
1433  // class (e.g. a Champions League page inheriting worldcuppage for styling), which would
1434  // otherwise light up the wrong More Comps entry.
1435  function _compActive(u) {
1436    var mc = /\/(?:fixtures|odds)\/([^\/]+)\//.exec(u || '');
1437    if (!mc) return false;
1438    var mp = /\/(?:fixtures|odds)\/([^\/]+)(?:\/|$)/.exec(location.pathname);
1439    return !!(mp && mp[1] === mc[1]);
1440  }
1441  // A flyout link is "in scope" when the current page sits within its section: exact match,
1442  // OR a section landing (href ends in '/') that prefixes the path, OR the section's outright
1443  // market when the page is a team-outright page (e.g. 'Team to win the Champions League').
1444  function _ddScope(u) {
1445    if (_ddAct(u)) return true;
1446    if (!u || u === '/') return false;
1447    var p = location.pathname;
1448    if (u.charAt(u.length - 1) === '/' && p.indexOf(u) === 0) {
1449      // The shared team-landing dirs (/odds/teams/, /fixtures/teams/) hold team pages from SEVERAL
1450      // competitions, so a foreign-section team page living there (e.g. an FA Cup "-facup" team page,
1451      // body .facuppage) must NOT light up the league Teams link - its own section's exact team link
1452      // highlights instead. (user 2026-09-14)
1453      if (/\/(?:odds|fixtures)\/teams\/$/.test(u) && document.body &&
1454          (document.body.classList.contains('facuppage') || document.body.classList.contains('worldcuppage'))) return false;
1455      return true;
1456    }
1457    if (/\/outright\.html$/.test(u)) {
1458      var seg = /\/(?:fixtures|odds)\/([^\/]+)\//.exec(u);
1459      if (seg && document.body && document.body.classList.contains('team-outright-page') && p.indexOf('/odds/' + seg[1] + '/') === 0) return true;
1460    }
1461    return false;
1462  }
1463  function buildDesktopDropdowns() {
1464    var ul = document.querySelector('.newmenu > ul');
1465    if (!ul || ul.getAttribute('data-dd-built')) return;
1466    ul.setAttribute('data-dd-built', '1');
1467    var lis = ul.children;
1468    for (var _x = lis.length - 1; _x >= 0; _x--) { var _xa = lis[_x].querySelector('a'); if (_xa && _navExcluded(_xa.textContent.trim())) lis[_x].parentNode.removeChild(lis[_x]); }
1469    function findLi(label) {
1470      for (var i = 0; i < lis.length; i++) { var a = lis[i].querySelector('a'); if (a && a.textContent.trim() === label) return lis[i]; }
1471      return null;
1472    }
1473    (typeof DESKTOP_STRUCTURE !== 'undefined' && DESKTOP_STRUCTURE.length ? DESKTOP_STRUCTURE : (typeof NAV_STRUCTURE !== 'undefined' ? NAV_STRUCTURE : [])).forEach(function (item) {
1474      if (!item || !item.items || !item.items.length) return;
1475      var li = findLi(item.label);
1476      if (!li || li.querySelector('ul')) return;
1477      li.classList.add('has-dd'); li.style.position = 'relative';
1478      var sub = document.createElement('ul'); sub.className = 'nm-dd';
1479      var anyActive = false;
1480      // 3rd level: leagues inside 'English Leagues' / 'More Comps' get a Matches/Odds flyout to their right.
1481      var derive = (item.label === 'English Leagues' || item.label === 'More Comps');
1482      item.items.forEach(function (c) {
1483        if (!c || !c.label) return;
1484        var sli = document.createElement('li'), sa = document.createElement('a');
1485        sa.href = c.href || '#'; sa.textContent = c.label;
1486        var hp = c.href || '';
1487        if (_ddAct(hp) || (item.label === 'More Comps' && _compActive(hp))) { sli.className = 'dd-active'; anyActive = true; }
1488        var fly = (c.items && c.items.length) ? c.items : null;
1489        if (!fly && derive) { var mm = /^\/fixtures\/([^\/]+)\/?$/.exec(hp); if (mm) { fly = [{ label: 'Matches', href: hp }, { label: 'Odds', href: '/odds/' + mm[1] + '/' }]; if (['championship', 'division-1', 'division-2'].indexOf(mm[1]) !== -1) fly.push({ label: 'Table', href: '/fixtures/' + mm[1] + '/table.html' }); } }
1490        if (fly && fly.length) {
1491          sli.className = (sli.className ? sli.className + ' ' : '') + 'has-fly';
1492          var fu = document.createElement('ul'); fu.className = 'nm-fly'; var childActive = false;
1493          fly.forEach(function (g) { if (!g || !g.label) return; var gl = document.createElement('li'), ga = document.createElement('a'); ga.href = g.href || '#'; ga.textContent = g.label; if (_ddScope(g.href)) { gl.className = 'dd-active'; anyActive = true; childActive = true; } gl.appendChild(ga); var _gcls = g['class'] || g.cls; if (_gcls) gl.className = (gl.className ? gl.className + ' ' : '') + _gcls; if (g.items && g.items.length) { gl.className = (gl.className ? gl.className + ' ' : '') + 'has-fly2'; var gu = document.createElement('ul'); gu.className = 'nm-fly2'; g.items.forEach(function (h) { if (!h || !h.label) return; var hl = document.createElement('li'), ha = document.createElement('a'); ha.href = h.href || '#'; ha.textContent = h.label; if (_ddScope(h.href)) { hl.className = 'dd-active'; anyActive = true; childActive = true; } hl.appendChild(ha); var _hcls = h['class'] || h.cls; if (_hcls) hl.className = (hl.className ? hl.className + ' ' : '') + _hcls; gu.appendChild(hl); }); gl.appendChild(gu); } fu.appendChild(gl); });
1494          if (childActive && sli.className.indexOf('dd-active') === -1) sli.className += ' dd-active'; sli.appendChild(sa); sli.appendChild(fu);
1495        } else { sli.appendChild(sa); }
1496        var _ccls = c['class'] || c.cls; if (_ccls) sli.className = (sli.className ? sli.className + ' ' : '') + _ccls;
1497        sub.appendChild(sli);
1498      });
1499      if (anyActive) li.classList.add('nm-parent-active');
1500      li.appendChild(sub);
1501    });
1502    if (!document.getElementById('nm-dd-style')) {
1503      var st = document.createElement('style'); st.id = 'nm-dd-style';
1504      st.textContent =
1505        '.newmenu .has-dd{position:relative;}' +
1506        '.newmenu .has-dd > ul.nm-dd{display:none;position:absolute;
1506left:0;top:100%;margin:0;padding:0;list-style:none;z-index:9999;border:2px solid #4d4a4a;border-radius:0 0 6px 6px;background:#fff;min-width:190px;box-shadow:0 4px 12px rgba(0,0,0,.18);overflow:visible;}' +
1507        '.newmenu .has-dd:hover > ul.nm-dd{display:block;}' +
1508        '.newmenu .has-dd > ul.nm-dd > li{float:none;margin:0;}' +
1509        '.newmenu .has-dd > ul.nm-dd > li > a{display:block;padding:9px 14px;font-size:10pt;color:#1b1b1b;border-bottom:1px solid #f0e2da;white-space:nowrap;}' +
1510        '.newmenu .has-dd > ul.nm-dd > li:last-child > a{border-bottom:0;}' +
1511        '.newmenu .has-dd > ul.nm-dd > li:nth-child(even) > a{background:#fbf7f1;}' +
1512        '.newmenu .has-dd > ul.nm-dd > li > a:hover{background:#4d4a4a;color:#fff;}' +
1513        '.newmenu .has-dd > ul.nm-dd > li.dd-active > a{background:#4d4a4a;color:#fff;}' +
1514        '.newmenu > ul > li.has-dd.nm-parent-active > a{background-color:#4d4a4a !important;}' +
1515        '.newmenu .nm-dd > li.has-fly{position:relative;}' +
1516        '.newmenu .nm-dd > li.has-fly > a::after{content:"\\203A";position:absolute;right:11px;opacity:.55;}' +
1517        '.newmenu .nm-dd > li.has-fly > ul.nm-fly{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;list-style:none;z-index:10000;border:2px solid #4d4a4a;border-radius:0 6px 6px 6px;background:#fff;min-width:132px;box-shadow:0 4px 12px rgba(0,0,0,.18);overflow:visible;}' +
1518        '.newmenu .nm-dd > li.has-fly:hover > ul.nm-fly{display:block;}' +
1519        '.newmenu ul.nm-fly > li{float:none;margin:0;}' +
1520        '.newmenu ul.nm-fly > li > a{display:block;padding:8px 16px;font-size:9.5pt;color:#1b1b1b;border-bottom:1px solid #f0e2da;white-space:nowrap;}' +
1521        '.newmenu ul.nm-fly > li:last-child > a{border-bottom:0;}' +
1522        '.newmenu ul.nm-fly > li > a:hover{background:#4d4a4a;color:#fff;}' +
1523        '.newmenu ul.nm-fly > li.dd-active > a{background:#4d4a4a;color:#fff;}' +
1524        '.newmenu ul.nm-fly > li.has-fly2{position:relative;}' +
1525        '.newmenu ul.nm-fly > li.has-fly2 > a::after{content:"\\203A";position:absolute;right:10px;opacity:.55;}' +
1526        '.newmenu ul.nm-fly2{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;list-style:none;z-index:10001;border:2px solid #4d4a4a;border-radius:0 6px 6px 6px;background:#fff;min-width:150px;box-shadow:0 4px 12px rgba(0,0,0,.18);}' +
1527        '.newmenu ul.nm-fly > li.has-fly2:hover > ul.nm-fly2{display:block;}' +
1528        '.newmenu ul.nm-fly2 > li{float:none;margin:0;}' +
1529        '.newmenu ul.nm-fly2 > li > a{display:block;padding:8px 15px;font-size:9.5pt;color:#1b1b1b;border-bottom:1px solid #f0e2da;white-space:nowrap;}' +
1530        '.newmenu ul.nm-fly2 > li:last-child > a{border-bottom:0;}' +
1531        '.newmenu ul.nm-fly2 > li > a:hover{background:#4d4a4a;color:#fff;}' +
1532        '.newmenu ul.nm-fly2 > li.dd-active > a{background:#4d4a4a;color:#fff;}' +
1533        /* indented team sub-links that belong to the Outright item (desktop dropdown) */
1534        '.newmenu li.nav-subitem > a{padding-left:30px !important;position:relative;}' +
1535        '.newmenu li.nav-subitem > a::before{content:"\\21B3";position:absolute;left:12px;top:50%;transform:translateY(-50%);opacity:.6;font-weight:400;}' +
1536        /* same indented team sub-links in the mobile hamburger drawer */
1537        '.fa-nav-drawer li.nav-subitem{margin-left:1.5em;}' +
1538        '.fa-nav-drawer li.nav-subitem > a{position:relative;padding-left:1.4em !important;}' +
1539        '.fa-nav-drawer li.nav-subitem > a::before{content:"\\21B3";position:absolute;left:0;opacity:.55;font-weight:400;}';
1540      document.head.appendChild(st);
1541    }
1542  }
1543
1544  function boot() {
1545    init();
1546    buildDesktopDropdowns();
1547    prependSubnavLabels();
1548    // Section links are now in place - align the subnav at DOMContentLoaded so
1549    // it lands under the right link without waiting for the load event.
1550    alignSectionSubnav();
1551  }
1552  if (document.readyState === 'loading') {
1553    document.addEventListener('DOMContentLoaded', boot);
1554  } else {
1555    boot();
1556  }
1557
1558  // ── Sub-nav alignment (SUBNAV_ALIGN_V2) ──────────────────────────────
1559  // CSS sets accurate padding-left defaults per body class so the subnav
1560  // renders in the correct position on first paint with no visible jump.
1561  // This helper is now only useful at resize time + on mobile (where the
1562  // hamburger replaces the nav and the offset should drop to 0). It is
1563  // INTENTIONALLY a no-op on initial desktop render to avoid disturbing
1564  // the CSS-painted position. We do not toggle opacity / .subnav-aligned
1565  // anymore (V4 of the CSS dropped the fade-in that was the user's "jump").
1566  function prependSubnavLabels() {
1567    var ul = document.querySelector('.subnav > ul'); if (!ul) return;
1568    if (ul.querySelector('.subnav-section-label')) return;
1569    var b = document.body.classList, label = null;
1570    if (b.contains('euro2028page')) label = 'Euro 28';
1571    else if (b.contains('serieapage')) label = 'Serie A';
1572    else if (b.contains('ligue1page')) label = 'French L1';
1573    // Champions League pages ALSO carry `worldcuppage` (so the subnav aligns under the World Cup /
1574    // More Comps main-nav link), so this MUST be checked BEFORE the worldcup case or the label reads
1575    // "World Cup" instead of "Champions League". (fixed 2026-08-28)
1576    else if (b.contains('championsleaguepage')) label = 'Champions League';
1577    else if (b.contains('worldcuppage') || b.contains('worldcupsection')) label = 'World Cup';
1578    else if (b.contains('bundesligapage')) label = 'Bundesliga';
1579    else if (b.contains('laligapage')) label = 'La Liga';
1580    else if (b.contains('championshippage')) label = 'Championship';
1581    else if (b.contains('division1page')) label = 'Division 1';
1582    else if (b.contains('division2page')) label = 'Division 2';
1583    if (!label) return;
1584    if (!document.getElementById('subnav-label-style')) {
1585      var st = document.createElement('style'); st.id = 'subnav-label-style';
1586      st.textContent = '.subnav ul li.subnav-section-label{cursor:default;} .subnav ul li.subnav-section-label .ssl-inner{display:block;background:#c12626;color:#fff;font-weight:700;font-size:12px;line-height:normal;padding:9px 10px 10px 12px;white-space:nowrap;text-transform:none;letter-spacing:0;} .subnav ul li.subnav-section-label .ssl-arrow{margin-left:4px;font-weight:400;opacity:.85;}';
1587      document.head.appendChild(st);
1588    }
1589    var li = document.createElement('li'); li.className = 'subnav-section-label';
1590    li.innerHTML = '<span class="ssl-inner">' + label + ' <span class="ssl-arrow">\u203A</span></span>';
1591    ul.insertBefore(li, ul.firstChild);
1592  }
1593  function alignSectionSubnav() {
1594    if (!document.body) return;
1595    if (document.body.classList.contains('padel-page')) return;
1596    var subnavUl = document.querySelector('.subnav > ul');
1597    if (!subnavUl) return;
1598    var anchorMap = [
1599      // Champions League pages also carry `facuppage` (for FA-Cup result
1600      // styling), so this MUST be checked first or the subnav would align
1601      // under the FA Cup link instead of the Champions League link.
1602      { body: 'championsleaguepage', anchor: '.newmenu .worldcuplink', center: true },
1603      { body: 'facuppage',     anchor: '.newmenu .facupoddslink' },
1604      // Championship pages get their own desktop top-menu link (added by
1605      // ensureChampionshipDesktopLink, championship pages only) immediately after
1606      // Premier League, so align the subnav under that "Championship" link. MUST
1607      // be checked before prempage (champ pages also carry prempage).
1608      { body: 'championshippage', anchor: '.newmenu .championshiplink' },
1609      // Division 1 & 2 sit under the same "English Leagues" dropdown as the
1610      // Championship and ALSO carry prempage, so anchor their subnav to the
1611      // English Leagues link and check BEFORE prempage (else they'd align under
1612      // the Premier League link instead of English Leagues).
1613      { body: 'division1page', anchor: '.newmenu .championshiplink' },
1614      { body: 'division2page', anchor: '.newmenu .championshiplink' },
1615      { body: 'worldcupsection', anchor: '.newmenu .worldcuplink', center: true },
1616      { body: 'worldcuppage', anchor: '.newmenu .worldcuplink', center: true },
1617      { body: 'euro2028page', anchor: '.newmenu .worldcuplink', center: true },
1618      // La Liga pages reuse the Premier League body classes for row styling, so they also
1619      // carry `prempage`. MUST be checked BEFORE prempage or the subnav aligns under the
1620      // Premier League link instead of La Liga.
1621      { body: 'bundesligapage', anchor: '.newmenu .worldcuplink', center: true },
1622      { body: 'laligapage', anchor: '.newmenu .worldcuplink', center: true },
1623      { body: 'serieapage', anchor: '.newmenu .worldcuplink', center: true },
1624      { body: 'ligue1page', anchor: '.newmenu .worldcuplink', center: true },
1625      { body: 'prempage',      anchor: '.newmenu .premierleaguelink' },
1626      // Betting pages: align the subnav under the "Betting" main-nav link.
1627      // Added 2026-07-10 - previously relied on a hardcoded style.css margin-left
1628      // which broke when the English Leagues nav link shifted Betting right.
1629      { body: 'bettingsection', anchor: '.newmenu .bettinglink' },
1630    ];
1631    for (var i = 0; i < anchorMap.length; i++) {
1632      var cfg = anchorMap[i];
1633      if (!document.body.classList.contains(cfg.body)) continue;
1634      var anchor = document.querySelector(cfg.anchor);
1635      if (!anchor || !anchor.offsetParent) {
1636        // Hamburger is active (mobile) - the CSS desktop padding-left would
1637        // push the subnav off-screen. Override to 0.
1638        subnavUl.style.setProperty('padding-left', '0', 'important');
1639        return;
1640      }
1641      // Desktop - leave the CSS default in place. Only override if the
1642      // measured offset differs meaningfully from current padding (i.e.
1643      // user resized the window and chrome reflowed the nav). NOTE:
1644      // getBoundingClientRect().left for a padded element is the BORDER
1645      // box edge (i.e. includes the padding), so aR.left - sR.left already
1646      // gives the correct padding-left value - do NOT add current.
1647      var aR = anchor.getBoundingClientRect();
1648      var sR = subnavUl.getBoundingClientRect();
1649      var current = parseInt(getComputedStyle(subnavUl).paddingLeft, 10) || 0;
1650      var measured;
1651      if (cfg.center) {
1652        var _kk = subnavUl.children, _cf = null, _cl = null, _r;
1653        for (var _a = 0; _a < _kk.length; _a++) { _r = _kk[_a].getBoundingClientRect(); if (_r.width > 0) { _cf = _r; break; } }
1654        for (var _b = _kk.length - 1; _b >= 0; _b--) { _r = _kk[_b].getBoundingClientRect(); if (_r.width > 0) { _cl = _r; break; } }
1655        var _cw = (_cf && _cl) ? (_cl.right - _cf.left) : 0, _ac = aR.left + aR.width / 2;
1656        measured = Math.max(8, Math.round(current + (_ac - _cw / 2 - (_cf ? _cf.left : sR.left))));
1657      } else {
1658        measured = Math.max(0, Math.round(aR.left - sR.left));
1659      }
1660      // Don't let the subnav run off the right edge. The World Cup subnav has many
1661      // items AND its menu word sits far right, so aligning under it overflows and
1662      // clips the tail (Teams / FAQ). If aligning under the anch
1662or would push the
1663      // last item past the viewport, shift the whole bar left JUST enough to fit,
1664      // keeping as much indent as the width allows (so it's not jammed fully left).
1665      // Sections that already fit keep their exact under-the-word alignment.
1666      // Clamp so the tail (Teams / FAQ) never runs off the right edge. Measure the
1667      // RIGHTMOST VISIBLE item, NOT subnavUl.scrollWidth: the Matches> / Odds>
1668      // dropdown flyouts are position:absolute and hugely inflate scrollWidth
1669      // (~1233px vs the ~825px of visible links), which made the old clamp think
1670      // the bar was almost viewport-wide and jam the indent to ~0 at 1280px.
1671      var _vw = document.documentElement.clientWidth || window.innerWidth || 0;
1672      var _kids = subnavUl.children, _lastRight = sR.right;
1673      for (var _k = _kids.length - 1; _k >= 0; _k--) {
1674        var _kr = _kids[_k].getBoundingClientRect();
1675        if (_kr.width > 0 && _kr.height > 0) { _lastRight = _kr.right; break; }
1676      }
1677      if (_vw > 0) {
1678        // Shifting padding-left by (measured - current) moves the last item's
1679        // right edge by the same delta. Keep it inside the viewport (14px gutter).
1680        var _projRight = _lastRight + (measured - current);
1681        var _over = _projRight - (_vw - 14);
1682        if (_over > 0) measured = Math.max(0, measured - _over);
1683      }
1684      if (Math.abs(measured - current) > 3) {
1685        subnavUl.style.setProperty('padding-left', measured + 'px', 'important');
1686      }
1687      return;
1688    }
1689  }
1690  // Run on resize ONLY. Initial paint relies on the CSS defaults so there
1691  // is zero JS-induced layout shift on page load.
1692  window.addEventListener('resize', alignSectionSubnav);
1693  window.addEventListener('load', alignSectionSubnav);   // belt-and-braces if user resized before load fires
1694})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.