FIFA World Cup 2026 — Brazil vs Morocco¶
Group C · Match 7 · June 13, 2026 · MetLife Stadium, East Rutherford, NJ
Final Score: Brazil 1 – 1 Morocco
All statistics sourced from ESPN, FIFA.com, Opta Analyst, FWC Times, NBC Sports, Sports Media Watch, and Morocco World News.
1. Imports & Theme Setup¶
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.gridspec as gridspec
import numpy as np
%matplotlib inline
# Colour palette
BRAZIL = "#009C3B" # Brazil green
BRAZIL2 = "#FFDF00" # Brazil yellow accent
MOROCCO = "#C1272D" # Morocco red
MOROCCO2 = "#006233" # Morocco dark-green accent
BG = "#0D1117"
PANEL = "#161B22"
TEXT = "#E6EDF3"
SUBTEXT = "#8B949E"
GRID = "#21262D"
plt.rcParams.update({
"figure.facecolor": BG,
"axes.facecolor": PANEL,
"axes.edgecolor": GRID,
"axes.labelcolor": TEXT,
"xtick.color": SUBTEXT,
"ytick.color": SUBTEXT,
"text.color": TEXT,
"grid.color": GRID,
"grid.linestyle": "--",
"grid.alpha": 0.5,
"font.family": "DejaVu Sans",
})
print("Theme applied.")
2. Match Data¶
All figures verified from official sources (ESPN, FIFA.com, Opta Analyst).
# Match statistics
stats_labels = ["Total Shots", "Shots on Target", "Corners", "Fouls", "Saves", "Duels Won"]
brazil_vals = [12, 5, 6, 16, 2, 53]
morocco_vals = [14, 3, 2, 14, 4, 62]
# Possession
poss_brazil = 51.3
poss_morocco = 48.7
# Passing
pass_brazil_tot = 524
pass_morocco_tot = 493
pass_brazil_acc = 87 # %
pass_morocco_acc = 86 # %
# Expected Goals
xg_brazil = 1.26
xg_morocco = 1.37
# Goals
goal_times = [21, 32]
goal_teams = ["Morocco\nSaibari", "Brazil\nVinicius Jr."]
goal_colors = [MOROCCO, BRAZIL]
# US Viewership — 2026 WC tournament data (Brazil vs Morocco ratings pending)
viewership_labels = [
"Mexico v\nS.Africa\n(FOX)",
"Mexico v\nS.Africa\n(Telemundo)",
"USA v\nParaguay\n(FOX)",
"USA v\nParaguay\n(Telemundo)",
]
viewership_vals = [6.31, 12.1, 15.99, 8.9]
view_colors = ["#1F6FEB", "#A371F7", "#1F6FEB", "#A371F7"]
# Attendance
attended = 80663
capacity = 82500
print("Data loaded.")
3. Helper Function¶
def hbar_pair(ax, labels, b_vals, m_vals, title):
"""Draw a grouped horizontal bar chart comparing Brazil vs Morocco."""
y = np.arange(len(labels))
h = 0.35
b1 = ax.barh(y + h/2, b_vals, h, color=BRAZIL, label="Brazil", zorder=3)
b2 = ax.barh(y - h/2, m_vals, h, color=MOROCCO, label="Morocco", zorder=3)
ax.set_yticks(y)
ax.set_yticklabels(labels, fontsize=10)
ax.set_title(title, fontsize=12, fontweight="bold", pad=8)
ax.grid(axis="x", zorder=0)
ax.legend(fontsize=9, loc="lower right")
max_v = max(b_vals + m_vals)
for bar, val in zip(b1, b_vals):
ax.text(bar.get_width() + max_v * 0.02,
bar.get_y() + bar.get_height() / 2,
str(val), va="center", fontsize=9, color=TEXT)
for bar, val in zip(b2, m_vals):
ax.text(bar.get_width() + max_v * 0.02,
bar.get_y() + bar.get_height() / 2,
str(val), va="center", fontsize=9, color=TEXT)
4. Full Dashboard¶
Renders all 8 panels in one figure:
- Header banner
- Match statistics comparison
- Ball possession donut
- Expected Goals (xG)
- Passes — volume & accuracy
- Goal timeline
- US viewership by match & network
- Stadium attendance gauge
fig = plt.figure(figsize=(20, 24), facecolor=BG)
gs = gridspec.GridSpec(4, 3, figure=fig, hspace=0.55, wspace=0.38)
# ── Panel 1: Header banner ───────────────────────────────────────────────────
ax_hdr = fig.add_subplot(gs[0, :])
ax_hdr.set_facecolor(PANEL)
ax_hdr.set_xlim(0, 1)
ax_hdr.set_ylim(0, 1)
ax_hdr.axis("off")
ax_hdr.text(0.5, 0.82, "FIFA World Cup 2026\u2122 \u00b7 Group C \u00b7 Match 7",
ha="center", va="center", fontsize=13, color=SUBTEXT)
ax_hdr.text(0.5, 0.50, "BRAZIL 1 \u2013 1 MOROCCO",
ha="center", va="center", fontsize=30, fontweight="bold", color=TEXT)
ax_hdr.text(0.5, 0.18,
"MetLife Stadium, East Rutherford, NJ \u00b7 June 13, 2026 \u00b7 Att: 80,663 / 82,500 (97.8%)",
ha="center", va="center", fontsize=11, color=SUBTEXT)
goal_text = "* 21' Ismael Saibari (Morocco) | 32' Vinicius Junior (Brazil) *"
ax_hdr.text(0.5, -0.04, goal_text, ha="center", va="center", fontsize=11, color=TEXT,
bbox=dict(boxstyle="round,pad=0.4", facecolor=GRID, edgecolor=GRID))
# ── Panel 2: Match statistics horizontal bars ────────────────────────────────
ax1 = fig.add_subplot(gs[1, :2])
hbar_pair(ax1, stats_labels, brazil_vals, morocco_vals, "Match Statistics Comparison")
# ── Panel 3: Possession donut ────────────────────────────────────────────────
ax2 = fig.add_subplot(gs[1, 2])
ax2.set_facecolor(PANEL)
ax2.pie([poss_brazil, poss_morocco], colors=[BRAZIL, MOROCCO], startangle=90,
wedgeprops=dict(width=0.5, edgecolor=BG, linewidth=2))
ax2.text(0, 0.12, f"{poss_brazil}%", ha="center", fontsize=16, fontweight="bold", color=BRAZIL)
ax2.text(0, -0.18, f"{poss_morocco}%", ha="center", fontsize=16, fontweight="bold", color=MOROCCO)
ax2.set_title("Ball Possession", fontsize=12, fontweight="bold", pad=10)
ax2.legend(["Brazil", "Morocco"], loc="lower center", fontsize=9, bbox_to_anchor=(0.5, -0.06))
# ── Panel 4: xG comparison ───────────────────────────────────────────────────
ax3 = fig.add_subplot(gs[2, 0])
bars = ax3.bar(["Brazil", "Morocco"], [xg_brazil, xg_morocco],
color=[BRAZIL, MOROCCO], width=0.45, zorder=3, edgecolor=BG, linewidth=1.5)
for bar, val in zip(bars, [xg_brazil, xg_morocco]):
ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.03,
f"{val:.2f}", ha="center", fontsize=13, fontweight="bold", color=TEXT)
ax3.set_ylim(0, max(xg_brazil, xg_morocco) * 1.35)
ax3.set_title("Expected Goals (xG)", fontsize=12, fontweight="bold")
ax3.set_ylabel("xG", fontsize=10)
ax3.grid(axis="y", zorder=0)
# ── Panel 5: Passes — volume & accuracy ─────────────────────────────────────
ax4 = fig.add_subplot(gs[2, 1])
ax4_r = ax4.twinx()
x, w = np.arange(2), 0.35
b_tot = ax4.bar(x - w/2, [pass_brazil_tot, pass_morocco_tot], w,
color=[BRAZIL, MOROCCO], zorder=3, edgecolor=BG, linewidth=1.2)
b_acc = ax4_r.bar(x + w/2, [pass_brazil_acc, pass_morocco_acc], w,
color=[BRAZIL2, MOROCCO2], zorder=3, edgecolor=BG, linewidth=1.2)
ax4.set_xticks(x)
ax4.set_xticklabels(["Brazil", "Morocco"])
ax4.set_ylabel("Total Passes", color=SUBTEXT, fontsize=9)
ax4_r.set_ylabel("Accuracy %", color=SUBTEXT, fontsize=9)
ax4.set_title("Passes \u2014 Volume & Accuracy", fontsize=12, fontweight="bold")
ax4.grid(axis="y", zorder=0)
for bar, val in zip(b_tot, [pass_brazil_tot, pass_morocco_tot]):
ax4.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5,
str(val), ha="center", fontsize=10, fontweight="bold", color=TEXT)
for bar, val in zip(b_acc, [pass_brazil_acc, pass_morocco_acc]):
ax4_r.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.3,
f"{val}%", ha="center", fontsize=10, fontweight="bold", color=TEXT)
ax4.legend(handles=[
mpatches.Patch(color=BRAZIL, label="Brazil passes"),
mpatches.Patch(color=MOROCCO, label="Morocco passes"),
mpatches.Patch(color=BRAZIL2, label="Brazil acc%"),
mpatches.Patch(color=MOROCCO2, label="Morocco acc%"),
], fontsize=8, loc="lower right")
# ── Panel 6: Goal timeline ───────────────────────────────────────────────────
ax5 = fig.add_subplot(gs[2, 2])
ax5.set_xlim(0, 95)
ax5.set_ylim(0, 1)
ax5.axhline(0.5, color=SUBTEXT, linewidth=2, zorder=1)
ax5.fill_between([0, 45], 0, 1, color=GRID, alpha=0.30)
ax5.fill_between([45, 90], 0, 1, color=GRID, alpha=0.15)
for t, label, color in zip(goal_times, goal_teams, goal_colors):
ax5.scatter(t, 0.5, s=280, color=color, zorder=5, edgecolors=BG, linewidths=1.5)
ax5.text(t, 0.72 if color == MOROCCO else 0.22,
f"{t}'", ha="center", fontsize=11, fontweight="bold", color=color)
ax5.text(t, 0.84 if color == MOROCCO else 0.10,
label, ha="center", fontsize=8, color=color)
ax5.axvline(45, color=SUBTEXT, linewidth=1, linestyle="--", alpha=0.6)
ax5.set_xticks([0, 15, 30, 45, 60, 75, 90])
ax5.set_xticklabels(["0'", "15'", "30'", "HT", "60'", "75'", "FT"])
ax5.set_yticks([])
ax5.set_title("Goal Timeline", fontsize=12, fontweight="bold")
ax5.legend(handles=[
mpatches.Patch(color=BRAZIL, label="Brazil"),
mpatches.Patch(color=MOROCCO, label="Morocco"),
], fontsize=9, loc="upper right")
# ── Panel 7: US Viewership ───────────────────────────────────────────────────
ax6 = fig.add_subplot(gs[3, :2])
x_v = np.arange(len(viewership_labels))
bars = ax6.bar(x_v, viewership_vals, color=view_colors, width=0.5,
zorder=3, edgecolor=BG, linewidth=1.5)
for bar, val in zip(bars, viewership_vals):
ax6.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.2,
f"{val}M", ha="center", fontsize=11, fontweight="bold", color=TEXT)
ax6.set_xticks(x_v)
ax6.set_xticklabels(viewership_labels, fontsize=10)
ax6.set_ylabel("US Viewers (millions)", fontsize=10)
ax6.set_title("2026 FIFA World Cup \u2014 US Viewership by Match & Network", fontsize=12, fontweight="bold")
ax6.grid(axis="y", zorder=0)
ax6.set_ylim(0, 20)
ax6.legend(handles=[
mpatches.Patch(color="#1F6FEB", label="FOX (English)"),
mpatches.Patch(color="#A371F7", label="Telemundo (Spanish)"),
], fontsize=10)
ax6.text(0.98, 0.95,
"Brazil vs Morocco specific ratings\nnot yet published (match Jun 13)",
transform=ax6.transAxes, ha="right", va="top", fontsize=8.5,
color=SUBTEXT, style="italic",
bbox=dict(boxstyle="round,pad=0.3", facecolor=GRID, edgecolor=GRID, alpha=0.7))
# ── Panel 8: Attendance gauge ────────────────────────────────────────────────
ax7 = fig.add_subplot(gs[3, 2])
ax7.set_facecolor(PANEL)
ax7.set_xlim(0, 1)
ax7.set_ylim(0, 1)
ax7.axis("off")
pct = attended / capacity
theta_bg = np.linspace(np.pi, 0, 200)
theta_fill = np.linspace(np.pi, np.pi - pct * np.pi, 200)
ax7.plot(0.5 + 0.38 * np.cos(theta_bg), 0.35 + 0.38 * np.sin(theta_bg),
color=GRID, linewidth=18, solid_capstyle="round")
ax7.plot(0.5 + 0.38 * np.cos(theta_fill), 0.35 + 0.38 * np.sin(theta_fill),
color=BRAZIL, linewidth=18, solid_capstyle="round")
ax7.text(0.5, 0.35, f"{attended:,}", ha="center", fontsize=20, fontweight="bold", color=TEXT)
ax7.text(0.5, 0.16, f"of {capacity:,} capacity", ha="center", fontsize=10, color=SUBTEXT)
ax7.text(0.5, 0.04, f"{pct*100:.1f}% full", ha="center", fontsize=13, fontweight="bold", color=BRAZIL2)
ax7.set_title("Stadium Attendance\nMetLife Stadium, NJ", fontsize=12, fontweight="bold")
# ── Footer ───────────────────────────────────────────────────────────────────
fig.text(0.5, 0.01,
"Sources: ESPN \u00b7 FIFA.com \u00b7 Opta Analyst \u00b7 FWC Times \u00b7 NBC Sports \u00b7 Sports Media Watch \u00b7 Morocco World News",
ha="center", fontsize=8.5, color=SUBTEXT, style="italic")
plt.savefig("/home/pravat/brazil_morocco_wc2026.png", dpi=150, bbox_inches="tight", facecolor=BG)
plt.show()
print("Dashboard saved to /home/pravat/brazil_morocco_wc2026.png")
5. Key Takeaways¶
| Metric | Winner |
|---|---|
| Total Shots | Morocco (14 vs 12) |
| Shots on Target | Brazil (5 vs 3) |
| xG | Morocco (1.37 vs 1.26) |
| Possession | Brazil (51.3% vs 48.7%) |
| Passes | Brazil (524 vs 493) |
| Duels Won | Morocco (62 vs 53) |
| Saves | Bounou/Morocco (4 vs 2) |
Morocco were the better team on the stats — more shots, higher xG, more duels won — but Alisson's double save in the dying minutes preserved Brazil's point.
Attendance: 80,663 / 82,500 (97.8% full) at MetLife Stadium.
Global audience for the 2026 World Cup is projected at 5–6 billion across the tournament, with the final expected to draw 1.6 billion+ viewers.
Sources: ESPN · FIFA.com · Opta Analyst · FWC Times · NBC Sports · Sports Media Watch · Morocco World News