Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions content/news/2026-03-14-german-open-third-place.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: "3rd place at RoboCup German Open 2026"
date: 2026-03-14
summary: "We got 3rd in the SPL at German Open, two weeks after first getting our robots."
author: "Team whIRLwind"
tags: ["competition", "german-open", "2026"]
---

We got 3rd in the SPL at the 2026 RoboCup German Open.

We had the robots for two weeks before the tournament. In that time we wrote everything that ran on them from scratch: the vision stack, the behaviours, and the RL policies for walking and ball handling. No code reused from previous years.

Two weeks is not a lot of time to get an RL policy to behave on a real robot, and it shows in some of our games. But it also held up well enough in others to put us on the podium, which we're pretty happy with given where we started.

Thanks to everyone on the team, and to the other teams and the organisers for the tournament.
16 changes: 5 additions & 11 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});
import nextCoreWebVitals from "eslint-config-next/core-web-vitals";
import nextTypeScript from "eslint-config-next/typescript";

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
...nextCoreWebVitals,
...nextTypeScript,
{
ignores: [
"node_modules/**",
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
"next_version/**",
],
},
];
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@
"lint": "eslint"
},
"dependencies": {
"gray-matter": "^4.0.3",
"next": "16.1.1",
"prettier": "^3.7.4",
"react": "19.2.3",
"react-dom": "19.2.3",
"remark": "^15.0.1",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
"remark-html": "^16.0.1",
"zod": "^4.3.5"
},
"devDependencies": {
Expand Down
844 changes: 842 additions & 2 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

173 changes: 79 additions & 94 deletions src/app/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import LinkButton from "@/components/LinkButton";
import type { Metadata } from "next";
import PageHero from "@/components/site/PageHero";
import { contactTopics, siteContact } from "@/lib/site-content";

export const metadata: Metadata = {
title: "Contact | Team whIRLwind",
Expand All @@ -8,116 +10,99 @@ export const metadata: Metadata = {

export default function ContactPage() {
return (
<section className="pb-20 pt-10 sm:pb-32 sm:pt-12">
<div className="mx-auto w-full max-w-[1120px] px-8 sm:px-10 lg:px-12 xl:px-4">
<div className="contact-header">
<h1 className="mt-0 text-[clamp(1.8rem,2.5vw,2.4rem)]">Contact</h1>
</div>

<div className="contact-layout">
<div className="contact-main">
<div className="contact-highlight">
<span className="contact-eyebrow">Membership update</span>
<h2 className="contact-highlight__title">
Applications temporarily closed
</h2>
<p className="contact-highlight__copy">
We&apos;re pausing new member applications while we continue
ramping up the team. We&apos;ll share an update on our channels
once we&apos;re ready to welcome new teammates again.
</p>
<div className="contact-highlight__actions">
<LinkButton
href="/socials"
label="Follow our socials"
bordered
/>
</div>
</div>
<div className="page-shell">
<PageHero
eyebrow="Contact"
title={
<>
Get in <span>touch.</span>
</>
}
description="Email us about sponsorships, research, demos, event invites, or press."
metrics={[
{ label: "Base", value: "Science Park" },
{ label: "Lab", value: "UvA IRL" },
{ label: "Status", value: "Applications paused" },
]}
actions={
<>
<a
href={`mailto:${siteContact.email}`}
className="link-button link-button--primary"
>
<span className="link-button__label">{siteContact.email}</span>
</a>
<LinkButton href="/socials" label="Follow updates" variant="secondary" />
</>
}
aside={
<div className="page-note">
<p>Membership update</p>
<h2>Applications are closed for now.</h2>
<span>
We&apos;ll post on our channels when we open them again.
</span>
</div>
}
/>

<div className="card contact-card">
<h3>Reach the team</h3>
<section className="site-section site-section--tight-top">
<div className="site-container contact-layout">
<div className="contact-stack">
<article className="detail-card">
<p className="detail-card__eyebrow">Contact</p>
<h2>Email</h2>
<a href={`mailto:${siteContact.email}`} className="detail-card__link">
{siteContact.email}
</a>
<p>
For collaborations, sponsorships, press, or general questions,
drop us a line and we&apos;ll connect you with the right person.
We&apos;ll get it to the right person.
</p>
<dl className="contact-details">
<div className="contact-detail">
<dt>Email</dt>
<dd>
<a
className="contact-link"
href="mailto:info@whirlwind.team"
>
info@whirlwind.team
</a>
</dd>
</div>
</dl>
</div>
</article>

<div className="card contact-card contact-card--topics">
<h3>How we can help</h3>
<div className="contact-topics">
<div>
<h4>Research & demos</h4>
<p>
Invite us to showcase humanoid robotics work or speak with
your students, lab, or community.
</p>
</div>
<div>
<h4>Sponsorship & support</h4>
<p>
Share how you&apos;d like to collaborate and we&apos;ll
explore ways to partner on upcoming competitions and events.
</p>
</div>
<div>
<h4>Media & press</h4>
<p>
Tell us your deadline and angle so we can coordinate
spokespeople, quotes, and assets.
</p>
</div>
<article className="detail-card">
<p className="detail-card__eyebrow">Topics</p>
<div className="topic-list">
{contactTopics.map((topic) => (
<section key={topic.title} className="topic-list__item">
<h3>{topic.title}</h3>
<p>{topic.description}</p>
</section>
))}
</div>
</div>
</article>
</div>

<aside className="contact-sidebar">
<div className="card contact-card">
<h3>Where we are</h3>
<p>
Team whIRLwind is based at the Intelligent Robotics Lab of the
University of Amsterdam.
</p>
<address className="contact-address">
Room L0.01
<br />
Science Park 900
<br />
1098 XH Amsterdam
<aside className="contact-stack">
<article className="detail-card">
<p className="detail-card__eyebrow">Location</p>
<h2>University of Amsterdam</h2>
<p>The Intelligent Robotics Lab at Science Park.</p>
<address className="detail-card__address">
{siteContact.addressLines.map((line) => (
<span key={line}>{line}</span>
))}
</address>
</div>
</article>

<div className="card contact-card">
<h3>Stay updated</h3>
<article className="detail-card">
<p className="detail-card__eyebrow">Follow</p>
<h2>Channels</h2>
<p>
Follow along for competition news and hear first when we reopen
member applications.
For updates and demos, follow us on our socials.
</p>
<div className="contact-actions">
<LinkButton href="/socials" label="Social channels" bordered />
<div className="detail-card__actions">
<LinkButton href="/socials" label="Socials" variant="secondary" />
<LinkButton
href="/publications"
label="Latest publications"
bordered
label="Publications"
variant="secondary"
/>
</div>
</div>
</article>
</aside>
</div>
</div>
</section>
</section>
</div>
);
}
Loading