A native iOS app, built by a coach, who needed fast and reliable game tracking without surrendering data to a third-party platform — designed for the sideline, built in Swift.
Coaching a youth soccer team generates a surprising amount of data. Scores, substitutions, player minutes, goals, assists, saves, cards. The problem isn’t that the data is hard to capture — it’s that every existing option forces a tradeoff I wasn’t willing to make. Pen and paper is fast but produces nothing useful afterward. Third-party apps are convenient but own your data, require accounts, and are built for organizations, not individual coaches. I wanted something purpose-built for how I actually work on a sideline: fast to set up, minimal interaction during play, and with data I control entirely.
+
+
+
+
+
+ set the roster - starters and substitutes
+
+
+
+
+
+
+
+ add game info - team size, location, date & time
+
+
I built SoccerGameTracker as a native iOS app in Swift — solo, from scratch. The easier path would have been a PWA or React Native wrapper; I’ve shipped both, and they’re faster to build. I chose Swift because a sideline app has a specific performance contract: launch instantly, respond without lag when a goal gets scored, never drop data if the phone gets pocketed. Native with local storage and no network dependency satisfies all three. No sync to worry about, no account to authenticate, no API that might be down at kickoff.
+
+
every decision went through one person - me
+
+
Constraint clarified scope. Without a backend, multi-device sync and shared rosters aren’t on the table — and that’s fine. Knowing what the app isn’t made it easier to build what it is. I handled both the design and implementation entirely, which meant every decision — from interaction model to data structure — went through one person. That’s mostly an advantage when you need to move fast and stay opinionated.
During a live game, a coach is watching players, calling substitutions, talking to parents. The app gets a glance and a thumb tap, not focused interaction. Every decision on the Live Game screen follows from that.
+
+
Pregame
+
+ The pre-game setup flow handles the overhead up front.
+ New Game collects opposition, date, location, and half duration — everything you'd put on a physical scoresheet — plus roster selection from a pre-built player list with jersey number and position.
+ The Game vs. Scrimmage toggle is a small but deliberate decision: scrimmages have different stakes and different lineup dynamics, and I wanted the history to reflect that distinction clearly.
+
+
+
+
Scoreboard & Quick Actions
+
+ The scoreboard and half timer sit at the top, readable in a second. The Quick Actions section — Team Goal and Opponent Goal — are the two largest tap targets in the app, sized and colored for immediate recognition in any lighting. Correction actions (Remove Team Goal, Subtract Goal) are present but visually subordinate: same area, smaller, lighter. Reachable when you need them, harder to hit by accident.
+
+
+
+
Players & Activity
+
+ Below the quick actions is where the app earns its depth. Each player in the active roster is tappable, opening a detail view where you can log individual stats in real time: goals, assists, saves, shots, yellow cards, red cards. Every action is timestamped and tied to the game half — building a full action log that the post-game summary renders as a timeline. That's the data that makes post-game review actually useful: not just who won, but who contributed, when, and how often.
+
+
+
+
+
+
+ Live Game screen showing scoreboard, timer, Quick Actions, and player list
+
+
Substitutions were a meaningful product decision. Mid-game sub support — with a distinct “substituted out” state separate from bench status — reflects how substitutions actually work: a player who comes off isn’t the same as a player who never started. Getting that distinction right matters for per-player stats to be accurate.
+
+
+
+
+
+
+ View a player's stats and position before substituting them on or off
+
+
The app is in active use this season, tested against the real constraint it was built for: a sideline, in real game conditions. Feedback from that use has been indispensable, with the per-player stats used between games to inform lineup and substitution planning. That’s the outcome I was building toward.
+
What real use has surfaced is mostly sequencing: the transition from pre-game setup into the live view needs less friction, and the post-game summary screen is still being refined. Those are the next iterations.
+
+
+
An App Store release is planned once the final rough edges are resolved. The app does one thing, does it reliably, and doesn’t ask for anything it doesn’t need.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/css/style.css b/docs/css/style.css
index b02231c0..489a9820 100644
--- a/docs/css/style.css
+++ b/docs/css/style.css
@@ -813,6 +813,10 @@ section {
justify-content: space-between !important;
}
+.justify-content-around {
+ justify-content: space-around !important;
+}
+
.justify-content-end {
justify-content: flex-end !important;
}
diff --git a/docs/css/style.css.map b/docs/css/style.css.map
index 8a674f8e..1b1c434e 100644
--- a/docs/css/style.css.map
+++ b/docs/css/style.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["../../src/sass/style.scss","../../src/sass/_variables.scss","../../src/sass/_animations.scss","../../src/sass/_fonts.scss","../../src/sass/_typography.scss","../../src/sass/_spacing.scss","../../src/sass/_layout.scss","../../src/sass/_lists.scss","../../src/sass/_highlight.scss","../../src/sass/_navigation.scss","../../src/sass/_footer.scss","../../src/sass/_badge.scss","../../src/sass/_buttons.scss","../../src/sass/_cards.scss","../../src/sass/_gallery.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;ACSA;AACE;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;;;AD3DF;EACE;EACA;EACA;;;AAGF;AACA;EACE;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAEF;AAAA;EAEE;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;EACA;;;AAGF;AACA;EACE;IACE;IACA;AAEA;AAEA;IACA;;AAGF;EACA;IACE;;EAEF;AAAA;AAAA;IAGE;IACA;IACA;;;AAGJ;AAAA;EAEE;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;;;AAGF;AACA;EACE;EACA;EACA,YACE,iDAC2B;;;AAE/B;EACE;EACA;EACA;;;AAGF;EAEE;EACA;EACA;;;AAEF;EACE;EACA;EACA,YACE,iDAC2B;;;AAE/B;EACE;;;AAGF;AACA;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;;AAGF;AEpKA;AAAA;AAAA;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;EACE;IAAI;;EACJ;IAAK;;EACL;IAAK;;EACL;IAAM;;;AAGR;EACE;IAAI;;EACJ;IAAK;;EACL;IAAK;;EACL;IAAM;;;AAGR;EACE;AAED;;;AAGD;EACE;AAED;;;AAGD;EACE;AAED;;;AC/CD;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AC7EF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EAEA;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAEF;AAAA;AAAA;AAAA;EAIE;EACA;;;AAEF;EACE;;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEF;EAIE;;;AAIJ;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;ACzPF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;ACjIF;AACA;EACE;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;IACE;;;AAGJ;EACE;IACE;;;AAGJ;EACE;;;AAEF;EACE;;;AAEF;EACE,SA1CK;;;AA4CP;EACE,gBA5CO;;;AA8CT;EACE,aA9CO;;;AAgDT;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;ACtEF;AAAA;AAAA;AAAA;AAAA;EAKE;EACA;;;ACNF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;ACVF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AAGF;EACI;IACA;IACA;IACA;;EAKF;IACE;;;ACvEJ;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;;;AAEF;AAAA;EAEE;;;AC9DF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;ACvBF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;;;AAMF;AACA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAMF;AACA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAKF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAMF;AACA;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EAIA;;;AC3IF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAIF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAIF;AACA;EACE;EACA;;;AAEF;EACE;;;AAIA;EACE;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;;;AAIJ;AACA;EACE;EACA;EACA;;;AAGF;AACA;EACE;;;AAEF;EACE;EACA;;;AAGF;EACE;IACE;;;AAIJ;EACE;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EAEA;;;AAEF;EACE;IACE;IAEA;;EAEF;IACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EAEA;;;AAEF;EACE;IACE;;;AAKJ;AAAA;EAEE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AC3RF;AACA;EACE;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;EACA;EACA;EAGA,YACE;EAEF;EACA;;AAEA;EACE;;AAGF;EAEE;EAGA;EACA,YACE;;AAIJ;EACE;EACA;;;AAKN;AA+BE;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAGF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EAhMA;EAmME;;AAjMF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAyLF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EAjPF;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAuOA;EAEE;;AAEA;EACE;EACA;;AAKN;EACE;EACA;EACA;EACA;;;AAGJ;AdvIA","file":"style.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["../../src/sass/style.scss","../../src/sass/_variables.scss","../../src/sass/_animations.scss","../../src/sass/_fonts.scss","../../src/sass/_typography.scss","../../src/sass/_spacing.scss","../../src/sass/_layout.scss","../../src/sass/_lists.scss","../../src/sass/_highlight.scss","../../src/sass/_navigation.scss","../../src/sass/_footer.scss","../../src/sass/_badge.scss","../../src/sass/_buttons.scss","../../src/sass/_cards.scss","../../src/sass/_gallery.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;ACSA;AACE;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;;;AD3DF;EACE;EACA;EACA;;;AAGF;AACA;EACE;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAEF;AAAA;EAEE;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;EACA;;;AAGF;AACA;EACE;IACE;IACA;AAEA;AAEA;IACA;;AAGF;EACA;IACE;;EAEF;AAAA;AAAA;IAGE;IACA;IACA;;;AAGJ;AAAA;EAEE;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;;;AAGF;AACA;EACE;EACA;EACA,YACE,iDAC2B;;;AAE/B;EACE;EACA;EACA;;;AAGF;EAEE;EACA;EACA;;;AAEF;EACE;EACA;EACA,YACE,iDAC2B;;;AAE/B;EACE;;;AAGF;AACA;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;;AAGF;AEpKA;AAAA;AAAA;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;EACE;IAAI;;EACJ;IAAK;;EACL;IAAK;;EACL;IAAM;;;AAGR;EACE;IAAI;;EACJ;IAAK;;EACL;IAAK;;EACL;IAAM;;;AAGR;EACE;AAED;;;AAGD;EACE;AAED;;;AAGD;EACE;AAED;;;AC/CD;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AC7EF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EAEA;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;;;AAGF;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAEF;AAAA;AAAA;AAAA;EAIE;EACA;;;AAEF;EACE;;AACA;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEF;EAIE;;;AAIJ;AACA;AAAA;EAEE;EACA;EACA;EACA;EACA;;;ACzPF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;ACjIF;AACA;EACE;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;IACE;;;AAGJ;EACE;IACE;;;AAGJ;EACE;;;AAEF;EACE;;;AAEF;EACE,SA7CK;;;AA+CP;EACE,gBA/CO;;;AAiDT;EACE,aAjDO;;;AAmDT;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;ACzEF;AAAA;AAAA;AAAA;AAAA;EAKE;EACA;;;ACNF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;ACVF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AAGF;EACI;IACA;IACA;IACA;;EAKF;IACE;;;ACvEJ;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;;;AAEF;AAAA;EAEE;;;AC9DF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;ACvBF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;AAAA;EAEE;EACA;;;AAMF;AACA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAMF;AACA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAKF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAMF;AACA;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EAIA;;;AC3IF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAIF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAIF;AACA;EACE;EACA;;;AAEF;EACE;;;AAIA;EACE;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;;;AAIJ;AACA;EACE;EACA;EACA;;;AAGF;AACA;EACE;;;AAEF;EACE;EACA;;;AAGF;EACE;IACE;;;AAIJ;EACE;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EAEA;;;AAEF;EACE;IACE;IAEA;;EAEF;IACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EAEA;;;AAEF;EACE;IACE;;;AAKJ;AAAA;EAEE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AC3RF;AACA;EACE;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;EACA;EACA;EAGA,YACE;EAEF;EACA;;AAEA;EACE;;AAGF;EAEE;EAGA;EACA,YACE;;AAIJ;EACE;EACA;;;AAKN;AA+BE;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAEF;EAGE;;AAGF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;AAEF;EACE;;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EAhMA;EAmME;;AAjMF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAyLF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EAjPF;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAuOA;EAEE;;AAEA;EACE;EACA;;AAKN;EACE;EACA;EACA;EACA;;;AAGJ;AdvIA","file":"style.css"}
\ No newline at end of file
diff --git a/docs/feed.json b/docs/feed.json
index 247a587c..e2f60ca6 100644
--- a/docs/feed.json
+++ b/docs/feed.json
@@ -26,6 +26,46 @@
"url": ""
},
"items": [
+ {
+ "id": "/development/archparser/",
+ "url": "/development/archparser/",
+ "title": "ArchParser: Architectural Drawing Analysis Platform",
+ "date_published": "2026-03-26T01:47:27Z",
+ "date": "2026-03-26T01:47:27Z"
+ }
+ ,
+ {
+ "id": "/designs/task-it/",
+ "url": "/designs/task-it/",
+ "title": "Task Management System",
+ "date_published": "2026-03-26T01:47:27Z",
+ "date": "2026-03-26T01:47:27Z"
+ }
+ ,
+ {
+ "id": "/designs/component-library/",
+ "url": "/designs/component-library/",
+ "title": "Component Library",
+ "date_published": "2026-03-26T01:47:27Z",
+ "date": "2026-03-26T01:47:27Z"
+ }
+ ,
+ {
+ "id": "/designs/alm/",
+ "url": "/designs/alm/",
+ "title": "Application Lifecycle Management",
+ "date_published": "2026-03-26T01:47:27Z",
+ "date": "2026-03-26T01:47:27Z"
+ }
+ ,
+ {
+ "id": "/case-studies/soccertracker/",
+ "url": "/case-studies/soccertracker/",
+ "title": "Soccer Game Tracker",
+ "date_published": "2026-03-26T01:32:09Z",
+ "date": "2026-03-26T01:32:09Z"
+ }
+ ,
{
"id": "/reference/",
"url": "/reference/",
@@ -90,14 +130,6 @@
"date": "2026-01-19T16:58:02Z"
}
,
- {
- "id": "/development/archparser/",
- "url": "/development/archparser/",
- "title": "ArchParser: Architectural Drawing Analysis Platform",
- "date_published": "2026-01-19T16:58:02Z",
- "date": "2026-01-19T16:58:02Z"
- }
- ,
{
"id": "/designs/customer-engagement/",
"url": "/designs/customer-engagement/",
@@ -122,30 +154,6 @@
"date": "2026-01-05T19:31:33Z"
}
,
- {
- "id": "/designs/task-it/",
- "url": "/designs/task-it/",
- "title": "Task Management System",
- "date_published": "2026-01-01T16:25:50Z",
- "date": "2026-01-01T16:25:50Z"
- }
- ,
- {
- "id": "/designs/component-library/",
- "url": "/designs/component-library/",
- "title": "Component Library",
- "date_published": "2026-01-01T16:25:50Z",
- "date": "2026-01-01T16:25:50Z"
- }
- ,
- {
- "id": "/designs/alm/",
- "url": "/designs/alm/",
- "title": "Application Lifecycle Management",
- "date_published": "2026-01-01T16:25:50Z",
- "date": "2026-01-01T16:25:50Z"
- }
- ,
{
"id": "/archives/",
"url": "/archives/",
diff --git a/docs/sass/style.css b/docs/sass/style.css
index 21c9b8b0..1db4dbe7 100644
--- a/docs/sass/style.css
+++ b/docs/sass/style.css
@@ -1 +1 @@
-:root{--font-family-sans:"Inter",system-ui,-apple-system,blinkmacsystemfont,"Segoe UI",roboto,"Helvetica Neue",arial,sans-serif;--font-family-serif:"Playfair Display",serif;--font-family-mono:"Fira Code","Courier New",courier,monospace;--font-family-heading:"Pirata One",cursive;--white:#f0f0f0;--black:#010101;--earth-dark:#2d1f12;--earth-brown:#4a3426;--earth-sage:#5a6b4f;--earth-sand:#c9b89a;--earth-cream:#f5f1e8;--accent-coral:#d35f3d;--accent-coral-dark:#b34a2d;--text-primary:#2d1f12;--text-secondary:#4a3426;--text-muted:#6b5d52;--shadow:rgba(45,31,18,.15);--shadow-heavy:rgba(45,31,18,.25);--shadow-light:rgba(45,31,18,.08);--font-size-xs:.75rem;--font-size-sm:.875rem;--font-size-md:1rem;--font-size-lg:1.125rem;--font-size-xl:1.25rem;--font-size-2xl:1.5rem;--font-size-3xl:2rem;--font-size-4xl:2.5rem;--font-size-5xl:3rem;--font-size-6xl:4rem;--space-2xs:.25rem;--space-xs:.5rem;--space-sm:.75rem;--space-md:1rem;--space-lg:1.5rem;--space-xl:2rem;--space-2xl:3rem;--space-3xl:6rem;--radius-sm:8px;--radius-md:12px;--radius-lg:16px;--radius-xl:20px;--radius-pill:50px;--border-thin:2px;--border-medium:4px;--border-thick:6px;--border-extra-thick:8px}*{box-sizing:border-box;margin:0;padding:0}:focus-visible{outline:var(--border-thin)solid var(--accent-coral);outline-offset:2px}html{scroll-behavior:smooth}body{font-family:var(--font-family-sans);background:linear-gradient(135deg,var(--earth-cream)0%,#e8dcc8 100%);color:var(--text-primary);min-height:100vh;line-height:1.6;position:relative}html::-webkit-scrollbar{width:16px}::-webkit-scrollbar{width:16px}html::-webkit-scrollbar-thumb{background:linear-gradient(var(--accent-coral-dark),var(--accent-coral));background-clip:content-box;border:2px solid transparent;border-radius:2rem}::-webkit-scrollbar-thumb{background:linear-gradient(var(--accent-coral-dark),var(--accent-coral));background-clip:content-box;border:2px solid transparent;border-radius:2rem}html::-webkit-scrollbar-track{border:1px solid var(--earth-sage);background:0 0;border-radius:2rem;padding:0 2px}::-webkit-scrollbar-track{border:1px solid var(--earth-sage);background:0 0;border-radius:2rem;padding:0 2px}@media (prefers-reduced-motion:reduce){.animated-element{opacity:1;transition:none;animation:none}body{scroll-behavior:auto}*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.display-none,.d-none{display:none!important}.hero{text-align:center;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;max-width:900px;margin:0 auto;padding:6rem 2rem;display:-ms-flexbox;display:flex}.rounded{border-radius:var(--radius-xl,20px)!important}.circle{border-radius:999px!important;padding:1rem!important}.fade-in{opacity:0;visibility:hidden;transition:opacity .5s ease-in,visibility 0s linear .5s}.fade-in.show,.show{opacity:1;visibility:visible;transition-delay:0s}.hide{opacity:0;visibility:hidden;transition:opacity .5s ease-in,visibility 0s linear .5s}.hide-on-screen{display:none!important}@media (max-width:768px){.hero h1{font-size:var(--font-size-5xl,3rem)}.no-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.hide-on-mobile{display:none!important}.work-grid,.callouts-grid{grid-template-columns:1fr}h2{font-size:var(--font-size-3xl,2rem)}}figcaption{font-size:var(--font-size-sm,.875rem);color:var(--text-secondary);text-align:center;font-style:italic;line-height:1.4}.arrows{width:60px;height:72px;margin-left:-30px;position:absolute;bottom:20px;left:50%}.arrows path{stroke:#bfe7fa;fill:transparent;stroke-width:1px;animation:2s infinite arrow}@keyframes arrow{0%{opacity:0}40%{opacity:1}80%{opacity:0}to{opacity:0}}.arrows path.a1{animation-delay:-1s}.arrows path.a2{animation-delay:-.5s}.arrows path.a3{animation-delay:0s}.pirata-one-regular{font-family:Pirata One,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue;font-style:normal;font-weight:400}.inter-400{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:400}.inter-500{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:500}.inter-600{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:600}.inter-700{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:700}.inter-800{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:800}.inter-900{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:900}.playfair-display-400{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:400}.playfair-display-500{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:500}.playfair-display-600{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:600}.playfair-display-700{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:700}.playfair-display-800{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:800}.playfair-display-900{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:900}h1,.text-h1{color:var(--text-primary);letter-spacing:2px;text-shadow:3px 3px 0 var(--accent-coral);font-family:Pirata One,cursive;font-size:max(2.5rem,min(8vw,5rem));line-height:1.2}.text-display{color:var(--text-primary);letter-spacing:2px;text-shadow:3px 3px 0 var(--accent-coral);font-family:Pirata One,cursive;font-size:max(3rem,min(10vw,6rem));line-height:1.1}.hero h1{font-family:var(--font-family-heading);color:var(--text-primary);text-shadow:3px 3px 0 var(--accent-coral);letter-spacing:2px;margin-bottom:1rem;line-height:1.2}h2,.text-h2{color:var(--text-primary);margin-bottom:var(--space-md,1.5rem);font-family:Playfair Display,serif;font-size:max(2rem,min(5vw,3rem));font-weight:700;line-height:1.3}.text-h2.no-underline:after{margin-bottom:var(--space-2xs,.25rem);display:none}h2:after,.text-h2:after{content:"";background:var(--accent-coral);border:3px solid var(--earth-brown);width:100px;height:5px;margin:1rem auto;display:block}h3,.text-h3{color:var(--text-primary);font-family:Playfair Display,serif;font-size:max(1.5rem,min(3vw,2rem));font-weight:600;line-height:1.3}h4,.text-h4{color:var(--text-primary);font-family:Playfair Display,serif;font-size:max(1.25rem,min(2.5vw,1.5rem));font-weight:600;line-height:1.4}h5,.text-h5{color:var(--text-primary);font-family:Inter,sans-serif;font-size:max(1.1rem,min(2vw,1.25rem));font-weight:700;line-height:1.4}h6,.text-h6{color:var(--text-primary);text-transform:uppercase;letter-spacing:.5px;font-family:Inter,sans-serif;font-size:1rem;font-weight:700;line-height:1.4}.text-body-lg{font-size:max(1.1rem,min(1.5vw,1.25rem))!important;line-height:1.7!important}p,.text-body{color:var(--text-primary);padding-bottom:var(--space-xs,.5rem);font-size:max(.95rem,min(1.5vw,1rem));line-height:1.7}p:last-child,.text-body:last-child{margin-bottom:0}.hero p{color:var(--earth-brown);max-width:720px;margin:0 auto;font-size:max(1.1rem,min(2vw,1.25rem))}.card-body p:first-of-type,.card-body .text-body:first-of-type{margin-top:var(--space-md,16px)}.text-body-sm{font-size:max(.85rem,min(1.2vw,.9rem));line-height:1.6}.text-caption{font-size:clamp(var(--space-sm),1vw,.85rem);color:var(--text-muted);line-height:1.5}.text-callout{color:var(--text-primary);margin:var(--space-lg,1.5rem);padding:var(--space-lg,1.5rem);background:var(--earth-sand-light);border-left:var(--border-medium)solid var(--accent-coral);font-size:max(1rem,min(1.5vw,1.1rem));font-weight:600;line-height:1.6}code,.code{font-family:var(--font-family-mono);font-size:max(.85rem,min(1vw,.9rem))}code.code-inline,.code.code-inline{background:var(--earth-sand);border-radius:var(--radius-sm);padding:.1rem .3rem}.code-block{background:var(--earth-sand);border-radius:var(--radius-md);border:var(--border-thin)solid var(--earth-sage);padding:1rem;font-size:max(.85rem,min(1vw,.9rem));display:block;overflow-x:auto}.text-muted{color:var(--text-muted)}.text-secondary{color:var(--text-secondary)}.text-accent{color:var(--accent-coral)}.text-semibold{font-weight:600}.text-bold{font-weight:700}.text-center{text-align:center}.lead,.text-lead{color:var(--text-secondary);font-size:max(1.1rem,min(2vw,1.35rem));line-height:1.7}a,.link{color:var(--earth-sage);border-bottom:1px solid var(--earth-sage);font-weight:var(--font-weight-semibold,600);text-decoration:none;transition:all .2s}a:hover,a:focus,.link:hover,.link:focus{color:var(--earth-brown);border-bottom-color:var(--earth-brown)}.link-brackets{margin-left:var(--space-md)!important}.link-brackets:after{content:"]";color:var(--earth-sage);margin-left:.2rem;transition:all .2s;position:absolute}.link-brackets:before{content:"[";color:var(--earth-sage);margin-left:-.8rem;transition:all .2s;position:absolute}.link-brackets:hover:after,.link-brackets:hover:before,.link-brackets:focus:after,.link-brackets:focus:before{color:var(--accent-coral)}blockquote,.blockquote{border-left:var(--border-thin)solid var(--earth-brown);padding-left:var(--space-md,1rem);margin:var(--space-lg,1.5rem)0;color:var(--text-secondary);font-style:italic}.mb-1{margin-bottom:var(--space-2xs)!important}.mb-2{margin-bottom:var(--space-xs)!important}.mb-3{margin-bottom:var(--space-sm)!important}.mb-4{margin-bottom:var(--space-md)!important}.mb-5{margin-bottom:var(--space-xl)!important}.mt-1{margin-top:var(--space-2xs)!important}.mt-2{margin-top:var(--space-xs)!important}.mt-3{margin-top:var(--space-sm)!important}.mt-4{margin-top:var(--space-md)!important}.mt-5{margin-top:var(--space-xl)!important}.ml-1{margin-left:var(--space-2xs)!important}.ml-2{margin-left:var(--space-xs)!important}.ml-3{margin-left:var(--space-sm)!important}.ml-4{margin-left:var(--space-md)!important}.ml-5{margin-left:var(--space-xl)!important}.mr-1{margin-right:var(--space-2xs)!important}.mr-2{margin-right:var(--space-xs)!important}.mr-3{margin-right:var(--space-sm)!important}.mr-4{margin-right:var(--space-md)!important}.mr-5{margin-right:var(--space-xl)!important}.p-1{padding:var(--space-2xs)!important}.p-2{padding:var(--space-xs)!important}.p-3{padding:var(--space-sm)!important}.p-4{padding:var(--space-md)!important}.p-5{padding:var(--space-xl)!important}.pt-1{padding-top:var(--space-2xs)!important}.pt-2{padding-top:var(--space-xs)!important}.pt-3{padding-top:var(--space-sm)!important}.pt-4{padding-top:var(--space-md)!important}.pt-5{padding-top:var(--space-xl)!important}.pb-1{padding-bottom:var(--space-2xs)!important}.pb-2{padding-bottom:var(--space-xs)!important}.pb-3{padding-bottom:var(--space-sm)!important}.pb-4{padding-bottom:var(--space-md)!important}.pb-5{padding-bottom:var(--space-xl)!important}.pl-1{padding-left:var(--space-2xs)!important}.pl-2{padding-left:var(--space-xs)!important}.pl-3{padding-left:var(--space-sm)!important}.pl-4{padding-left:var(--space-md)!important}.pl-5{padding-left:var(--space-xl)!important}.pr-1{padding-right:var(--space-2xs)!important}.pr-2{padding-right:var(--space-xs)!important}.pr-3{padding-right:var(--space-sm)!important}.pr-4{padding-right:var(--space-md)!important}.pr-5{padding-right:var(--space-xl)!important}section{max-width:1200px;padding:var(--space-3xl,6rem)var(--space-lg,2rem);margin:0 auto}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.row{gap:var(--space-lg,2rem);-ms-flex-wrap:wrap;flex-wrap:wrap;display:-ms-flexbox;display:flex}@media (min-width:768px){.row{gap:var(--space-sm,.5rem)}.card-body .row{gap:0}}.button-row{gap:1rem}.no-wrap{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.d-flex{display:-ms-flexbox;display:flex}.flex-column{-ms-flex-direction:column;flex-direction:column}.align-items-center{-ms-flex-align:center;align-items:center}.work-grid{gap:var(--space-lg,2rem);margin-bottom:var(--space-lg,2rem);grid-template-columns:repeat(auto-fit,minmax(300px,1fr));display:grid}.divider{width:50%;height:var(--space-md,1.5rem);background:var(--accent-coral);border:var(--border-medium,4px)outset var(--earth-dark);margin:0 auto;display:block}.divider.vertical{width:var(--space-2xs,.25rem);background:var(--earth-dark);border-radius:var(--radius-sm);height:100%;margin:0;border:none!important}ul,ol,.card-body ul,.card-body ol,ul.list,ol.list{margin-left:var(--space-lg)!important;padding-left:var(--space-md)!important}.highlight-block{-ms-flex-direction:row;flex-direction:row;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}.header-highlight{text-align:center;margin:var(--space-md)auto;padding:var(--space-md);width:100%}nav{z-index:1000;backdrop-filter:blur(24px);filter:url(#lensFilter)saturate(120%)brightness(1.15);border:var(--border-medium,4px)solid var(--earth-brown);padding:var(--space-sm)2rem;box-shadow:0 8px 24px var(--shadow-heavy);background:rgba(245,241,232,.5);border-radius:50px;position:fixed;top:2rem;left:50%;transform:translate(-50%)}.main-nav{width:fit-content;margin:0 auto;position:sticky;top:2rem;left:0;right:0;transform:none!important}nav ul{-ms-flex-pack:center;justify-content:center;gap:2rem;list-style:none;display:-ms-flexbox;display:flex;margin-left:0!important;padding-left:0!important}nav a{color:var(--text-primary);padding:var(--space-2xs)var(--space-xs,.5rem);border-bottom:none;font-size:.95rem;font-weight:500;text-decoration:none;transition:color .3s}nav a:hover,nav a:focus{color:var(--accent-coral);outline:var(--border-thin)solid var(--accent-coral);outline-offset:2px}nav a.active{color:var(--accent-coral)}nav a.sub-page{outline:var(--border-thin)solid var(--text-secondary);outline-offset:2px}nav a.sub-page:hover{outline-color:var(--accent-coral)}@media (max-width:768px){nav{top:var(--space-sm,1rem)!important;padding:var(--space-xs,.5rem)var(--space-sm,1rem)!important;margin:0 auto!important}nav ul{gap:var(--space-sm,1rem)}}footer{background:var(--earth-dark);color:var(--earth-cream);padding:var(--space-2xl,3rem)var(--space-xl,2rem);margin-top:var(--space-3xl,6rem);border-top:var(--border-extra-thick,8px)solid var(--earth-brown);box-shadow:0 -10px 30px var(--shadow)}.footer-content{grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:3rem;max-width:1200px;margin:0 auto;display:grid}.footer-section h3{color:var(--accent-coral);margin-bottom:var(--space-md,1rem);font-family:Playfair Display,serif;font-size:max(1.2rem,min(2vw,1.5rem))}.footer-section h4{margin-bottom:var(--space-md,1rem);font-family:Playfair Display,serif;font-size:max(1.1rem,min(1.25vw,1.125rem));color:rgba(255,255,255,.5)!important}.footer-section p,.footer-section li{color:var(--earth-cream);margin-bottom:var(--space-xs,.5rem);line-height:1.6}.footer-section ul{padding:0;list-style:none}.footer-section li{margin-bottom:var(--space-xs,.5rem)}.footer-bottom{text-align:center;margin-top:var(--space-2xl,3rem);padding-top:var(--space-xl,2rem);border-top:var(--border-medium,4px)solid var(--earth-brown);color:var(--earth-sand)}.footer-bottom p{font-size:var(--font-size-sm);color:var(--earth-cream);margin:0}.footer-section a,.footer-bottom a{color:var(--earth-cream);text-decoration:underline}.footer-section a:hover,.footer-bottom a:hover{color:var(--accent-coral)}.badge{padding:var(--space-2xs,.25rem)var(--space-sm,.75rem);background:var(--earth-sage);color:var(--earth-cream);border:var(--border-thin,2px)solid var(--earth-brown);border-radius:var(--radius-pill);font-size:.85rem;font-weight:600;display:inline-block}.badge-accent{background:var(--accent-coral-dark)}.badge-outline{color:inherit;background:0 0}.badges,.row-badges{gap:var(--space-2xs,.25rem);-ms-flex-wrap:wrap;flex-wrap:wrap;display:-ms-flexbox;display:flex}.btn{padding:var(--space-sm,.75rem)var(--space-lg,1.5rem);font-family:var(--font-family-sans);font-size:var(--font-size-md,1rem);border-radius:var(--radius-md,12px);cursor:pointer;text-align:center;border:none;font-weight:600;text-decoration:none;transition:all .2s;display:inline-block}.btn:hover,.btn:focus{box-shadow:2px 2px 0 var(--earth-brown);transform:translate(2px,2px)}.btn-primary{background:var(--accent-coral-dark);color:var(--white);border:var(--border-medium,4px)solid var(--earth-brown);box-shadow:4px 4px 0 var(--earth-brown)}.btn-primary:hover,.btn-primary:focus{background:var(--accent-coral-dark);color:var(--white)}.btn-secondary{background:var(--earth-cream);color:var(--text-primary);border:var(--border-medium,4px)solid var(--earth-brown);box-shadow:4px 4px 0 var(--earth-sage)}.btn-secondary:hover,.btn-secondary:focus{background:var(--earth-sand);color:var(--text-primary);box-shadow:2px 2px 0 var(--earth-sage);outline:var(--border-thin)solid var(--earth-dark);transform:translate(2px,2px)}.btn-outline{backdrop-filter:blur(24px);filter:url(#lensFilter)saturate(120%)brightness(1.15);color:var(--text-primary)!important;border:var(--border-medium,4px)solid var(--earth-brown)!important;box-shadow:none!important;background:rgba(255,255,255,.5)!important}.btn-outline:hover,.btn-outline:focus{color:var(--earth-cream);text-decoration:underline}.btn-sm{padding:var(--space-xs,.5rem)var(--space-md,1rem)!important;border-width:var(--border-thin)!important;font-size:.9rem!important}.btn-lg{padding:var(--space-md,1rem)var(--space-xl,2rem)!important;font-size:1.1rem!important}.skip-link{background:var(--accent-coral);color:var(--white);padding:var(--space-sm,.75rem)var(--space-lg,1.5rem);border:var(--border-medium,4px)solid var(--earth-brown);z-index:2000;clip:rect(0,0,0,0);font-weight:600;text-decoration:none;position:absolute;top:-100px;left:0;overflow:hidden}.skip-link:focus{clip:auto;top:1rem;left:1rem;overflow:visible}.social-links{-ms-flex-wrap:wrap;flex-wrap:wrap;gap:1rem;display:-ms-flexbox;display:flex}.social-link{padding:var(--space-xs,.5rem)var(--space-md,1rem);background:var(--earth-sage);color:var(--white);border:var(--border-medium,4px)solid var(--earth-sand);border-radius:8px;font-size:.95rem;font-weight:500;transition:all .3s;display:inline-block;text-decoration:none!important}.social-link:hover,.social-link:focus{background:var(--accent-coral);transform:translateY(-2px);color:var(--white)!important}.card{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);padding:var(--space-lg,1.5rem);box-shadow:8px 8px 0 var(--shadow-heavy);transition:transform .3s,box-shadow .3s}.card:hover{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.card:focus-within{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.card-layered{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);padding:var(--space-xl,2rem);box-shadow:12px 12px 0 var(--earth-sage),12px 12px 0 5px var(--earth-brown)}.card-shadow{background:var(--earth-cream);border-radius:var(--radius-lg,16px);padding:var(--space-md,1.5rem);box-shadow:10px 10px 0 var(--shadow-heavy);border:none}.card-accent{background:var(--earth-cream);border:var(--border-extra-thick,8px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);box-shadow:16px 16px 0 var(--accent-coral),16px 16px 0 6px var(--earth-brown);overflow:hidden}.card-flex{-ms-flex-direction:column;flex-direction:column;display:-ms-flexbox;display:flex}.card-flex p{-ms-flex:1 0;flex:1 0}.card-with-columns .row{-ms-flex-align:start;align-items:flex-start;gap:var(--space-md,1rem);-ms-flex-item-align:stretch;align-self:stretch;display:-ms-flexbox;display:flex}.card-with-columns .column{-ms-flex:1 0 0;flex:1 0 0;-ms-flex-item-align:stretch;align-self:stretch;-ms-flex-align:start;align-items:flex-start;display:-ms-flexbox;display:flex}.card-header{margin-bottom:var(--space-md,1rem);padding-bottom:var(--space-md,1rem);border-bottom:var(--border-thin,2px)solid var(--earth-sand)}.card-body{margin-bottom:var(--space-md)}.card-body img{object-fit:cover;width:100%}@media (min-width:768px){.img-thumbnail{max-width:50%}}.card-body:last-child{margin-bottom:0}.card-footer{margin-top:var(--space-md,1rem);padding-top:var(--space-md,1rem);border-top:var(--border-thin,2px)solid var(--earth-sand)}.showcase-content{padding:var(--space-lg,2rem)}.showcase-content h3{font-family:var(--font-family-serif);color:var(--text-primary);margin-bottom:var(--space-sm,1rem);font-size:max(1.3rem,min(2.5vw,1.8rem))}.showcase-content p{color:var(--text-secondary);margin-bottom:var(--space-md,1.5rem);font-size:max(.95rem,min(1.5vw,1rem))}.showcase-small{background:var(--earth-sand);border:4px solid var(--earth-brown);border-radius:var(--radius-lg,16px);padding:var(--space-md,1.5rem);box-shadow:6px 6px 0 var(--shadow-heavy);transition:transform .3s}.showcase-small:hover{transform:translateY(-4px)}.showcase-small:focus-within{transform:translateY(-4px)}.showcase-small h4{font-family:var(--font-family-serif);color:var(--text-primary);margin-bottom:var(--space-xs,.5rem);font-size:max(1.1rem,min(2vw,1.3rem))}.showcase-small p{color:var(--text-secondary);font-size:max(.9rem,min(1.5vw,.95rem))}.about-card{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);padding:var(--space-2xl,3rem);border-radius:var(--border-radius-xl,20px);box-shadow:12px 12px 0 var(--earth-sage),12px 12px 0 5px var(--earth-brown);margin:0 auto}@media (max-width:768px){.about-card{box-shadow:6px 6px 0 var(--earth-sage),6px 6px 0 4px var(--earth-brown);padding:var(--space-md,1rem)}.about-grid{grid-template-columns:1fr!important}}.about-card p{color:var(--text-primary);margin-bottom:var(--space-lg,1.5rem);font-size:max(1rem,min(1.5vw,1.1rem));line-height:1.8}.about-card p:last-child{margin-bottom:0}.about-grid{gap:var(--space-xl,2rem);margin-top:var(--space-xl,2rem);grid-template-columns:repeat(auto-fit,minmax(350px,1fr));display:grid}.showcase-large{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);box-shadow:12px 12px 0 var(--accent-coral),12px 12px 0 6px var(--earth-brown);transition:transform .3s;overflow:hidden}@media (max-width:768px){.showcase-large{box-shadow:6px 6px 0 var(--accent-coral),6px 6px 0 4px var(--earth-brown)}}.showcase-large:hover{transform:translateY(-8px)}.showcase-large:focus-within{transform:translateY(-8px)}.showcase-image{background:linear-gradient(135deg,var(--earth-sage)0%,var(--earth-sand)100%);width:100%;height:300px;padding:var(--space-md,1rem);font-size:var(--font-size-5xl,3rem);color:var(--earth-cream);border-bottom:var(--border-thick,6px)solid var(--earth-brown);object-fit:contain;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}.showcase-image-dark{background:linear-gradient(135deg,var(--black)0%,var(--earth-dark)100%)}.small-showcase-cards{gap:var(--space-md,1.5rem);margin-top:var(--space-sm,1rem);grid-column:1/-1;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));display:grid}.callouts-grid{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:2rem;margin-top:2rem;display:grid}.callout{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--accent-coral-dark);border-radius:var(--radius-xl,20px);box-shadow:8px 8px 0 var(--shadow-heavy);-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:justify;justify-content:space-between;-ms-flex-align:end;align-items:flex-end;padding:2rem;transition:transform .3s,box-shadow .3s;display:-ms-flexbox;display:flex;position:relative}.callout:hover{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.callout:focus-within{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.callout:before{content:"\"";font-family:var(--font-family-serif);font-size:var(--font-size-6xl,4rem);color:var(--accent-coral);line-height:1;position:absolute;top:10px;left:20px}.callout-text{margin-bottom:var(--space-md,1.5rem);padding-top:var(--space-sm,1rem);color:var(--text-primary);font-style:italic}.callout-author{color:var(--text-primary);font-weight:600;display:block}.callout-role{font-size:var(--font-size-sm,.9rem);color:var(--text-secondary)}.gallery{--size:100px;grid-template-columns:repeat(6,var(--size));grid-auto-rows:var(--size);margin-bottom:var(--size);-ms-flex-align:start;align-items:start;justify-items:center;gap:5px;display:grid}.gallery:has(:hover) picture:not(:hover){filter:brightness(.5)contrast(.5)}.gallery:has(:focus) picture:not(:focus){filter:brightness(.5)contrast(.5)}.gallery picture{object-fit:cover;width:calc(var(--size)*2);height:calc(var(--size)*2);clip-path:path("M90,10 C100,0 100,0 110,10 190,90 190,90 190,90 200,100 200,100 190,110 190,110 110,190 110,190 100,200 100,200 90,190 90,190 10,110 10,110 0,100 0,100 10,90Z");border-radius:5px;grid-column:auto/span 2;transition:clip-path .25s,filter .75s}.gallery picture:nth-child(5n-1){grid-column:2/span 2}.gallery picture:hover,.gallery picture:focus{clip-path:path("M0,0 C0,0 200,0 200,0 200,0 200,100 200,100 200,100 200,200 200,200 200,200 100,200 100,200 100,200 100,200 0,200 0,200 0,100 0,100 0,100 0,100 0,100Z");z-index:1;transition:clip-path .25s,filter .25s}.gallery picture:focus{outline-offset:-5px;outline:1px dashed #000}.carousel>input{clip:rect(1px,1px,1px,1px);clip-path:inset(50%);width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.carousel>input:nth-of-type(15):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1400%}.carousel>input:nth-of-type(14):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1300%}.carousel>input:nth-of-type(13):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1200%}.carousel>input:nth-of-type(12):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1100%}.carousel>input:nth-of-type(11):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1000%}.carousel>input:nth-of-type(10):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-900%}.carousel>input:nth-of-type(9):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-800%}.carousel>input:nth-of-type(8):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-700%}.carousel>input:nth-of-type(7):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-600%}.carousel>input:nth-of-type(6):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-500%}.carousel>input:nth-of-type(5):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-400%}.carousel>input:nth-of-type(4):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-300%}.carousel>input:nth-of-type(3):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-200%}.carousel>input:nth-of-type(2):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-100%}.carousel>input:first-of-type:checked~.carousel__slides .carousel__slide:first-of-type{margin-left:0%}.carousel>input:first-of-type:checked~.carousel__thumbnails li:first-of-type,.carousel>input:nth-of-type(2):checked~.carousel__thumbnails li:nth-of-type(2),.carousel>input:nth-of-type(3):checked~.carousel__thumbnails li:nth-of-type(3),.carousel>input:nth-of-type(4):checked~.carousel__thumbnails li:nth-of-type(4),.carousel>input:nth-of-type(5):checked~.carousel__thumbnails li:nth-of-type(5),.carousel>input:nth-of-type(6):checked~.carousel__thumbnails li:nth-of-type(6),.carousel>input:nth-of-type(7):checked~.carousel__thumbnails li:nth-of-type(7),.carousel>input:nth-of-type(8):checked~.carousel__thumbnails li:nth-of-type(8),.carousel>input:nth-of-type(9):checked~.carousel__thumbnails li:nth-of-type(9),.carousel>input:nth-of-type(10):checked~.carousel__thumbnails li:nth-of-type(10),.carousel>input:nth-of-type(11):checked~.carousel__thumbnails li:nth-of-type(11),.carousel>input:nth-of-type(12):checked~.carousel__thumbnails li:nth-of-type(12),.carousel>input:nth-of-type(13):checked~.carousel__thumbnails li:nth-of-type(13),.carousel>input:nth-of-type(14):checked~.carousel__thumbnails li:nth-of-type(14),.carousel>input:nth-of-type(15):checked~.carousel__thumbnails li:nth-of-type(15){box-shadow:0 0 0 5px rgba(0,0,255,.5)}.carousel__slides{z-index:1;white-space:nowrap;box-sizing:border-box;margin:0;padding:0;display:-ms-flexbox;display:flex;position:relative;overflow:hidden}.carousel__slide{vertical-align:top;box-sizing:border-box;white-space:normal;-ms-flex:1 0 100%;flex:1 0 100%;width:100%;height:100%;transition:all .3s ease-out;display:block;position:relative;overflow:hidden}.carousel__slide figure{-ms-flex-direction:column;flex-direction:column;margin:0;display:-ms-flexbox;display:flex}.carousel__slide div{width:100%;position:relative}.carousel__slide div:before{content:"";width:100%;padding-top:66.6667%;display:block}.carousel__slide div>img{width:100%;height:100%;position:absolute;top:0;bottom:0;left:0;right:0}.carousel__slide img{object-fit:cover;-ms-flex:auto;flex:auto;display:block}.carousel__slide figcaption{-ms-flex:none;flex:none;-ms-flex-item-align:center;align-self:center;width:25%;min-width:150px;padding:20px 20px 0}.carousel__slide .credit{color:rgba(0,0,0,.5);margin-top:1rem;display:block}.carousel__slide.scrollable{overflow-y:scroll}.carousel__thumbnails{margin:0 -10px;padding:0;list-style:none;display:-ms-flexbox;display:flex}.carousel__slides+.carousel__thumbnails{margin-top:20px}.carousel__thumbnails li{-ms-flex:auto;flex:auto;max-width:calc(16.6667% - 20px);margin:0 10px;transition:all .3s ease-in-out}.carousel__thumbnails label{display:block;position:relative}.carousel__thumbnails label:before{content:"";width:100%;padding-top:100%;display:block}.carousel__thumbnails label>img{width:100%;height:100%;position:absolute;top:0;bottom:0;left:0;right:0}.carousel__thumbnails label:hover,.carousel__thumbnails label:focus{cursor:pointer}.carousel__thumbnails label:hover img,.carousel__thumbnails label:focus img{transition:all .3s ease-in-out;box-shadow:0 0 0 1px rgba(0,0,0,.25)}.carousel__thumbnails img{object-fit:cover;width:100%;height:100%;display:block}
\ No newline at end of file
+:root{--font-family-sans:"Inter",system-ui,-apple-system,blinkmacsystemfont,"Segoe UI",roboto,"Helvetica Neue",arial,sans-serif;--font-family-serif:"Playfair Display",serif;--font-family-mono:"Fira Code","Courier New",courier,monospace;--font-family-heading:"Pirata One",cursive;--white:#f0f0f0;--black:#010101;--earth-dark:#2d1f12;--earth-brown:#4a3426;--earth-sage:#5a6b4f;--earth-sand:#c9b89a;--earth-cream:#f5f1e8;--accent-coral:#d35f3d;--accent-coral-dark:#b34a2d;--text-primary:#2d1f12;--text-secondary:#4a3426;--text-muted:#6b5d52;--shadow:rgba(45,31,18,.15);--shadow-heavy:rgba(45,31,18,.25);--shadow-light:rgba(45,31,18,.08);--font-size-xs:.75rem;--font-size-sm:.875rem;--font-size-md:1rem;--font-size-lg:1.125rem;--font-size-xl:1.25rem;--font-size-2xl:1.5rem;--font-size-3xl:2rem;--font-size-4xl:2.5rem;--font-size-5xl:3rem;--font-size-6xl:4rem;--space-2xs:.25rem;--space-xs:.5rem;--space-sm:.75rem;--space-md:1rem;--space-lg:1.5rem;--space-xl:2rem;--space-2xl:3rem;--space-3xl:6rem;--radius-sm:8px;--radius-md:12px;--radius-lg:16px;--radius-xl:20px;--radius-pill:50px;--border-thin:2px;--border-medium:4px;--border-thick:6px;--border-extra-thick:8px}*{box-sizing:border-box;margin:0;padding:0}:focus-visible{outline:var(--border-thin)solid var(--accent-coral);outline-offset:2px}html{scroll-behavior:smooth}body{font-family:var(--font-family-sans);background:linear-gradient(135deg,var(--earth-cream)0%,#e8dcc8 100%);color:var(--text-primary);min-height:100vh;line-height:1.6;position:relative}html::-webkit-scrollbar{width:16px}::-webkit-scrollbar{width:16px}html::-webkit-scrollbar-thumb{background:linear-gradient(var(--accent-coral-dark),var(--accent-coral));background-clip:content-box;border:2px solid transparent;border-radius:2rem}::-webkit-scrollbar-thumb{background:linear-gradient(var(--accent-coral-dark),var(--accent-coral));background-clip:content-box;border:2px solid transparent;border-radius:2rem}html::-webkit-scrollbar-track{border:1px solid var(--earth-sage);background:0 0;border-radius:2rem;padding:0 2px}::-webkit-scrollbar-track{border:1px solid var(--earth-sage);background:0 0;border-radius:2rem;padding:0 2px}@media (prefers-reduced-motion:reduce){.animated-element{opacity:1;transition:none;animation:none}body{scroll-behavior:auto}*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.display-none,.d-none{display:none!important}.hero{text-align:center;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;max-width:900px;margin:0 auto;padding:6rem 2rem;display:-ms-flexbox;display:flex}.rounded{border-radius:var(--radius-xl,20px)!important}.circle{border-radius:999px!important;padding:1rem!important}.fade-in{opacity:0;visibility:hidden;transition:opacity .5s ease-in,visibility 0s linear .5s}.fade-in.show,.show{opacity:1;visibility:visible;transition-delay:0s}.hide{opacity:0;visibility:hidden;transition:opacity .5s ease-in,visibility 0s linear .5s}.hide-on-screen{display:none!important}@media (max-width:768px){.hero h1{font-size:var(--font-size-5xl,3rem)}.no-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.hide-on-mobile{display:none!important}.work-grid,.callouts-grid{grid-template-columns:1fr}h2{font-size:var(--font-size-3xl,2rem)}}figcaption{font-size:var(--font-size-sm,.875rem);color:var(--text-secondary);text-align:center;font-style:italic;line-height:1.4}.arrows{width:60px;height:72px;margin-left:-30px;position:absolute;bottom:20px;left:50%}.arrows path{stroke:#bfe7fa;fill:transparent;stroke-width:1px;animation:2s infinite arrow}@keyframes arrow{0%{opacity:0}40%{opacity:1}80%{opacity:0}to{opacity:0}}.arrows path.a1{animation-delay:-1s}.arrows path.a2{animation-delay:-.5s}.arrows path.a3{animation-delay:0s}.pirata-one-regular{font-family:Pirata One,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue;font-style:normal;font-weight:400}.inter-400{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:400}.inter-500{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:500}.inter-600{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:600}.inter-700{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:700}.inter-800{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:800}.inter-900{font-optical-sizing:auto;font-family:Inter,sans-serif;font-style:normal;font-weight:900}.playfair-display-400{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:400}.playfair-display-500{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:500}.playfair-display-600{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:600}.playfair-display-700{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:700}.playfair-display-800{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:800}.playfair-display-900{font-optical-sizing:auto;font-family:Playfair Display,serif;font-style:normal;font-weight:900}h1,.text-h1{color:var(--text-primary);letter-spacing:2px;text-shadow:3px 3px 0 var(--accent-coral);font-family:Pirata One,cursive;font-size:max(2.5rem,min(8vw,5rem));line-height:1.2}.text-display{color:var(--text-primary);letter-spacing:2px;text-shadow:3px 3px 0 var(--accent-coral);font-family:Pirata One,cursive;font-size:max(3rem,min(10vw,6rem));line-height:1.1}.hero h1{font-family:var(--font-family-heading);color:var(--text-primary);text-shadow:3px 3px 0 var(--accent-coral);letter-spacing:2px;margin-bottom:1rem;line-height:1.2}h2,.text-h2{color:var(--text-primary);margin-bottom:var(--space-md,1.5rem);font-family:Playfair Display,serif;font-size:max(2rem,min(5vw,3rem));font-weight:700;line-height:1.3}.text-h2.no-underline:after{margin-bottom:var(--space-2xs,.25rem);display:none}h2:after,.text-h2:after{content:"";background:var(--accent-coral);border:3px solid var(--earth-brown);width:100px;height:5px;margin:1rem auto;display:block}h3,.text-h3{color:var(--text-primary);font-family:Playfair Display,serif;font-size:max(1.5rem,min(3vw,2rem));font-weight:600;line-height:1.3}h4,.text-h4{color:var(--text-primary);font-family:Playfair Display,serif;font-size:max(1.25rem,min(2.5vw,1.5rem));font-weight:600;line-height:1.4}h5,.text-h5{color:var(--text-primary);font-family:Inter,sans-serif;font-size:max(1.1rem,min(2vw,1.25rem));font-weight:700;line-height:1.4}h6,.text-h6{color:var(--text-primary);text-transform:uppercase;letter-spacing:.5px;font-family:Inter,sans-serif;font-size:1rem;font-weight:700;line-height:1.4}.text-body-lg{font-size:max(1.1rem,min(1.5vw,1.25rem))!important;line-height:1.7!important}p,.text-body{color:var(--text-primary);padding-bottom:var(--space-xs,.5rem);font-size:max(.95rem,min(1.5vw,1rem));line-height:1.7}p:last-child,.text-body:last-child{margin-bottom:0}.hero p{color:var(--earth-brown);max-width:720px;margin:0 auto;font-size:max(1.1rem,min(2vw,1.25rem))}.card-body p:first-of-type,.card-body .text-body:first-of-type{margin-top:var(--space-md,16px)}.text-body-sm{font-size:max(.85rem,min(1.2vw,.9rem));line-height:1.6}.text-caption{font-size:clamp(var(--space-sm),1vw,.85rem);color:var(--text-muted);line-height:1.5}.text-callout{color:var(--text-primary);margin:var(--space-lg,1.5rem);padding:var(--space-lg,1.5rem);background:var(--earth-sand-light);border-left:var(--border-medium)solid var(--accent-coral);font-size:max(1rem,min(1.5vw,1.1rem));font-weight:600;line-height:1.6}code,.code{font-family:var(--font-family-mono);font-size:max(.85rem,min(1vw,.9rem))}code.code-inline,.code.code-inline{background:var(--earth-sand);border-radius:var(--radius-sm);padding:.1rem .3rem}.code-block{background:var(--earth-sand);border-radius:var(--radius-md);border:var(--border-thin)solid var(--earth-sage);padding:1rem;font-size:max(.85rem,min(1vw,.9rem));display:block;overflow-x:auto}.text-muted{color:var(--text-muted)}.text-secondary{color:var(--text-secondary)}.text-accent{color:var(--accent-coral)}.text-semibold{font-weight:600}.text-bold{font-weight:700}.text-center{text-align:center}.lead,.text-lead{color:var(--text-secondary);font-size:max(1.1rem,min(2vw,1.35rem));line-height:1.7}a,.link{color:var(--earth-sage);border-bottom:1px solid var(--earth-sage);font-weight:var(--font-weight-semibold,600);text-decoration:none;transition:all .2s}a:hover,a:focus,.link:hover,.link:focus{color:var(--earth-brown);border-bottom-color:var(--earth-brown)}.link-brackets{margin-left:var(--space-md)!important}.link-brackets:after{content:"]";color:var(--earth-sage);margin-left:.2rem;transition:all .2s;position:absolute}.link-brackets:before{content:"[";color:var(--earth-sage);margin-left:-.8rem;transition:all .2s;position:absolute}.link-brackets:hover:after,.link-brackets:hover:before,.link-brackets:focus:after,.link-brackets:focus:before{color:var(--accent-coral)}blockquote,.blockquote{border-left:var(--border-thin)solid var(--earth-brown);padding-left:var(--space-md,1rem);margin:var(--space-lg,1.5rem)0;color:var(--text-secondary);font-style:italic}.mb-1{margin-bottom:var(--space-2xs)!important}.mb-2{margin-bottom:var(--space-xs)!important}.mb-3{margin-bottom:var(--space-sm)!important}.mb-4{margin-bottom:var(--space-md)!important}.mb-5{margin-bottom:var(--space-xl)!important}.mt-1{margin-top:var(--space-2xs)!important}.mt-2{margin-top:var(--space-xs)!important}.mt-3{margin-top:var(--space-sm)!important}.mt-4{margin-top:var(--space-md)!important}.mt-5{margin-top:var(--space-xl)!important}.ml-1{margin-left:var(--space-2xs)!important}.ml-2{margin-left:var(--space-xs)!important}.ml-3{margin-left:var(--space-sm)!important}.ml-4{margin-left:var(--space-md)!important}.ml-5{margin-left:var(--space-xl)!important}.mr-1{margin-right:var(--space-2xs)!important}.mr-2{margin-right:var(--space-xs)!important}.mr-3{margin-right:var(--space-sm)!important}.mr-4{margin-right:var(--space-md)!important}.mr-5{margin-right:var(--space-xl)!important}.p-1{padding:var(--space-2xs)!important}.p-2{padding:var(--space-xs)!important}.p-3{padding:var(--space-sm)!important}.p-4{padding:var(--space-md)!important}.p-5{padding:var(--space-xl)!important}.pt-1{padding-top:var(--space-2xs)!important}.pt-2{padding-top:var(--space-xs)!important}.pt-3{padding-top:var(--space-sm)!important}.pt-4{padding-top:var(--space-md)!important}.pt-5{padding-top:var(--space-xl)!important}.pb-1{padding-bottom:var(--space-2xs)!important}.pb-2{padding-bottom:var(--space-xs)!important}.pb-3{padding-bottom:var(--space-sm)!important}.pb-4{padding-bottom:var(--space-md)!important}.pb-5{padding-bottom:var(--space-xl)!important}.pl-1{padding-left:var(--space-2xs)!important}.pl-2{padding-left:var(--space-xs)!important}.pl-3{padding-left:var(--space-sm)!important}.pl-4{padding-left:var(--space-md)!important}.pl-5{padding-left:var(--space-xl)!important}.pr-1{padding-right:var(--space-2xs)!important}.pr-2{padding-right:var(--space-xs)!important}.pr-3{padding-right:var(--space-sm)!important}.pr-4{padding-right:var(--space-md)!important}.pr-5{padding-right:var(--space-xl)!important}section{max-width:1200px;padding:var(--space-3xl,6rem)var(--space-lg,2rem);margin:0 auto}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.row{gap:var(--space-lg,2rem);-ms-flex-wrap:wrap;flex-wrap:wrap;display:-ms-flexbox;display:flex}@media (min-width:768px){.row{gap:var(--space-sm,.5rem)}.card-body .row{gap:0}}.button-row{gap:1rem}.no-wrap{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.d-flex{display:-ms-flexbox;display:flex}.flex-column{-ms-flex-direction:column;flex-direction:column}.align-items-center{-ms-flex-align:center;align-items:center}.work-grid{gap:var(--space-lg,2rem);margin-bottom:var(--space-lg,2rem);grid-template-columns:repeat(auto-fit,minmax(300px,1fr));display:grid}.divider{width:50%;height:var(--space-md,1.5rem);background:var(--accent-coral);border:var(--border-medium,4px)outset var(--earth-dark);margin:0 auto;display:block}.divider.vertical{width:var(--space-2xs,.25rem);background:var(--earth-dark);border-radius:var(--radius-sm);height:100%;margin:0;border:none!important}ul,ol,.card-body ul,.card-body ol,ul.list,ol.list{margin-left:var(--space-lg)!important;padding-left:var(--space-md)!important}.highlight-block{-ms-flex-direction:row;flex-direction:row;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}.header-highlight{text-align:center;margin:var(--space-md)auto;padding:var(--space-md);width:100%}nav{z-index:1000;backdrop-filter:blur(24px);filter:url(#lensFilter)saturate(120%)brightness(1.15);border:var(--border-medium,4px)solid var(--earth-brown);padding:var(--space-sm)2rem;box-shadow:0 8px 24px var(--shadow-heavy);background:rgba(245,241,232,.5);border-radius:50px;position:fixed;top:2rem;left:50%;transform:translate(-50%)}.main-nav{width:fit-content;margin:0 auto;position:sticky;top:2rem;left:0;right:0;transform:none!important}nav ul{-ms-flex-pack:center;justify-content:center;gap:2rem;list-style:none;display:-ms-flexbox;display:flex;margin-left:0!important;padding-left:0!important}nav a{color:var(--text-primary);padding:var(--space-2xs)var(--space-xs,.5rem);border-bottom:none;font-size:.95rem;font-weight:500;text-decoration:none;transition:color .3s}nav a:hover,nav a:focus{color:var(--accent-coral);outline:var(--border-thin)solid var(--accent-coral);outline-offset:2px}nav a.active{color:var(--accent-coral)}nav a.sub-page{outline:var(--border-thin)solid var(--text-secondary);outline-offset:2px}nav a.sub-page:hover{outline-color:var(--accent-coral)}@media (max-width:768px){nav{top:var(--space-sm,1rem)!important;padding:var(--space-xs,.5rem)var(--space-sm,1rem)!important;margin:0 auto!important}nav ul{gap:var(--space-sm,1rem)}}footer{background:var(--earth-dark);color:var(--earth-cream);padding:var(--space-2xl,3rem)var(--space-xl,2rem);margin-top:var(--space-3xl,6rem);border-top:var(--border-extra-thick,8px)solid var(--earth-brown);box-shadow:0 -10px 30px var(--shadow)}.footer-content{grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:3rem;max-width:1200px;margin:0 auto;display:grid}.footer-section h3{color:var(--accent-coral);margin-bottom:var(--space-md,1rem);font-family:Playfair Display,serif;font-size:max(1.2rem,min(2vw,1.5rem))}.footer-section h4{margin-bottom:var(--space-md,1rem);font-family:Playfair Display,serif;font-size:max(1.1rem,min(1.25vw,1.125rem));color:rgba(255,255,255,.5)!important}.footer-section p,.footer-section li{color:var(--earth-cream);margin-bottom:var(--space-xs,.5rem);line-height:1.6}.footer-section ul{padding:0;list-style:none}.footer-section li{margin-bottom:var(--space-xs,.5rem)}.footer-bottom{text-align:center;margin-top:var(--space-2xl,3rem);padding-top:var(--space-xl,2rem);border-top:var(--border-medium,4px)solid var(--earth-brown);color:var(--earth-sand)}.footer-bottom p{font-size:var(--font-size-sm);color:var(--earth-cream);margin:0}.footer-section a,.footer-bottom a{color:var(--earth-cream);text-decoration:underline}.footer-section a:hover,.footer-bottom a:hover{color:var(--accent-coral)}.badge{padding:var(--space-2xs,.25rem)var(--space-sm,.75rem);background:var(--earth-sage);color:var(--earth-cream);border:var(--border-thin,2px)solid var(--earth-brown);border-radius:var(--radius-pill);font-size:.85rem;font-weight:600;display:inline-block}.badge-accent{background:var(--accent-coral-dark)}.badge-outline{color:inherit;background:0 0}.badges,.row-badges{gap:var(--space-2xs,.25rem);-ms-flex-wrap:wrap;flex-wrap:wrap;display:-ms-flexbox;display:flex}.btn{padding:var(--space-sm,.75rem)var(--space-lg,1.5rem);font-family:var(--font-family-sans);font-size:var(--font-size-md,1rem);border-radius:var(--radius-md,12px);cursor:pointer;text-align:center;border:none;font-weight:600;text-decoration:none;transition:all .2s;display:inline-block}.btn:hover,.btn:focus{box-shadow:2px 2px 0 var(--earth-brown);transform:translate(2px,2px)}.btn-primary{background:var(--accent-coral-dark);color:var(--white);border:var(--border-medium,4px)solid var(--earth-brown);box-shadow:4px 4px 0 var(--earth-brown)}.btn-primary:hover,.btn-primary:focus{background:var(--accent-coral-dark);color:var(--white)}.btn-secondary{background:var(--earth-cream);color:var(--text-primary);border:var(--border-medium,4px)solid var(--earth-brown);box-shadow:4px 4px 0 var(--earth-sage)}.btn-secondary:hover,.btn-secondary:focus{background:var(--earth-sand);color:var(--text-primary);box-shadow:2px 2px 0 var(--earth-sage);outline:var(--border-thin)solid var(--earth-dark);transform:translate(2px,2px)}.btn-outline{backdrop-filter:blur(24px);filter:url(#lensFilter)saturate(120%)brightness(1.15);color:var(--text-primary)!important;border:var(--border-medium,4px)solid var(--earth-brown)!important;box-shadow:none!important;background:rgba(255,255,255,.5)!important}.btn-outline:hover,.btn-outline:focus{color:var(--earth-cream);text-decoration:underline}.btn-sm{padding:var(--space-xs,.5rem)var(--space-md,1rem)!important;border-width:var(--border-thin)!important;font-size:.9rem!important}.btn-lg{padding:var(--space-md,1rem)var(--space-xl,2rem)!important;font-size:1.1rem!important}.skip-link{background:var(--accent-coral);color:var(--white);padding:var(--space-sm,.75rem)var(--space-lg,1.5rem);border:var(--border-medium,4px)solid var(--earth-brown);z-index:2000;clip:rect(0,0,0,0);font-weight:600;text-decoration:none;position:absolute;top:-100px;left:0;overflow:hidden}.skip-link:focus{clip:auto;top:1rem;left:1rem;overflow:visible}.social-links{-ms-flex-wrap:wrap;flex-wrap:wrap;gap:1rem;display:-ms-flexbox;display:flex}.social-link{padding:var(--space-xs,.5rem)var(--space-md,1rem);background:var(--earth-sage);color:var(--white);border:var(--border-medium,4px)solid var(--earth-sand);border-radius:8px;font-size:.95rem;font-weight:500;transition:all .3s;display:inline-block;text-decoration:none!important}.social-link:hover,.social-link:focus{background:var(--accent-coral);transform:translateY(-2px);color:var(--white)!important}.card{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);padding:var(--space-lg,1.5rem);box-shadow:8px 8px 0 var(--shadow-heavy);transition:transform .3s,box-shadow .3s}.card:hover{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.card:focus-within{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.card-layered{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);padding:var(--space-xl,2rem);box-shadow:12px 12px 0 var(--earth-sage),12px 12px 0 5px var(--earth-brown)}.card-shadow{background:var(--earth-cream);border-radius:var(--radius-lg,16px);padding:var(--space-md,1.5rem);box-shadow:10px 10px 0 var(--shadow-heavy);border:none}.card-accent{background:var(--earth-cream);border:var(--border-extra-thick,8px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);box-shadow:16px 16px 0 var(--accent-coral),16px 16px 0 6px var(--earth-brown);overflow:hidden}.card-flex{-ms-flex-direction:column;flex-direction:column;display:-ms-flexbox;display:flex}.card-flex p{-ms-flex:1 0;flex:1 0}.card-with-columns .row{-ms-flex-align:start;align-items:flex-start;gap:var(--space-md,1rem);-ms-flex-item-align:stretch;align-self:stretch;display:-ms-flexbox;display:flex}.card-with-columns .column{-ms-flex:1 0 0;flex:1 0 0;-ms-flex-item-align:stretch;align-self:stretch;-ms-flex-align:start;align-items:flex-start;display:-ms-flexbox;display:flex}.card-header{margin-bottom:var(--space-md,1rem);padding-bottom:var(--space-md,1rem);border-bottom:var(--border-thin,2px)solid var(--earth-sand)}.card-body{margin-bottom:var(--space-md)}.card-body img{object-fit:cover;width:100%}@media (min-width:768px){.img-thumbnail{max-width:50%}}.card-body:last-child{margin-bottom:0}.card-footer{margin-top:var(--space-md,1rem);padding-top:var(--space-md,1rem);border-top:var(--border-thin,2px)solid var(--earth-sand)}.showcase-content{padding:var(--space-lg,2rem)}.showcase-content h3{font-family:var(--font-family-serif);color:var(--text-primary);margin-bottom:var(--space-sm,1rem);font-size:max(1.3rem,min(2.5vw,1.8rem))}.showcase-content p{color:var(--text-secondary);margin-bottom:var(--space-md,1.5rem);font-size:max(.95rem,min(1.5vw,1rem))}.showcase-small{background:var(--earth-sand);border:4px solid var(--earth-brown);border-radius:var(--radius-lg,16px);padding:var(--space-md,1.5rem);box-shadow:6px 6px 0 var(--shadow-heavy);transition:transform .3s}.showcase-small:hover{transform:translateY(-4px)}.showcase-small:focus-within{transform:translateY(-4px)}.showcase-small h4{font-family:var(--font-family-serif);color:var(--text-primary);margin-bottom:var(--space-xs,.5rem);font-size:max(1.1rem,min(2vw,1.3rem))}.showcase-small p{color:var(--text-secondary);font-size:max(.9rem,min(1.5vw,.95rem))}.about-card{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);padding:var(--space-2xl,3rem);border-radius:var(--border-radius-xl,20px);box-shadow:12px 12px 0 var(--earth-sage),12px 12px 0 5px var(--earth-brown);margin:0 auto}@media (max-width:768px){.about-card{box-shadow:6px 6px 0 var(--earth-sage),6px 6px 0 4px var(--earth-brown);padding:var(--space-md,1rem)}.about-grid{grid-template-columns:1fr!important}}.about-card p{color:var(--text-primary);margin-bottom:var(--space-lg,1.5rem);font-size:max(1rem,min(1.5vw,1.1rem));line-height:1.8}.about-card p:last-child{margin-bottom:0}.about-grid{gap:var(--space-xl,2rem);margin-top:var(--space-xl,2rem);grid-template-columns:repeat(auto-fit,minmax(350px,1fr));display:grid}.showcase-large{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--earth-brown);border-radius:var(--radius-xl,20px);box-shadow:12px 12px 0 var(--accent-coral),12px 12px 0 6px var(--earth-brown);transition:transform .3s;overflow:hidden}@media (max-width:768px){.showcase-large{box-shadow:6px 6px 0 var(--accent-coral),6px 6px 0 4px var(--earth-brown)}}.showcase-large:hover{transform:translateY(-8px)}.showcase-large:focus-within{transform:translateY(-8px)}.showcase-image{background:linear-gradient(135deg,var(--earth-sage)0%,var(--earth-sand)100%);width:100%;height:300px;padding:var(--space-md,1rem);font-size:var(--font-size-5xl,3rem);color:var(--earth-cream);border-bottom:var(--border-thick,6px)solid var(--earth-brown);object-fit:contain;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}.showcase-image-dark{background:linear-gradient(135deg,var(--black)0%,var(--earth-dark)100%)}.small-showcase-cards{gap:var(--space-md,1.5rem);margin-top:var(--space-sm,1rem);grid-column:1/-1;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));display:grid}.callouts-grid{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:2rem;margin-top:2rem;display:grid}.callout{background:var(--earth-cream);border:var(--border-thick,6px)solid var(--accent-coral-dark);border-radius:var(--radius-xl,20px);box-shadow:8px 8px 0 var(--shadow-heavy);-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:justify;justify-content:space-between;-ms-flex-align:end;align-items:flex-end;padding:2rem;transition:transform .3s,box-shadow .3s;display:-ms-flexbox;display:flex;position:relative}.callout:hover{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.callout:focus-within{box-shadow:12px 12px 0 var(--shadow-heavy);transform:translateY(-5px)}.callout:before{content:"\"";font-family:var(--font-family-serif);font-size:var(--font-size-6xl,4rem);color:var(--accent-coral);line-height:1;position:absolute;top:10px;left:20px}.callout-text{margin-bottom:var(--space-md,1.5rem);padding-top:var(--space-sm,1rem);color:var(--text-primary);font-style:italic}.callout-author{color:var(--text-primary);font-weight:600;display:block}.callout-role{font-size:var(--font-size-sm,.9rem);color:var(--text-secondary)}.gallery{--size:100px;grid-template-columns:repeat(6,var(--size));grid-auto-rows:var(--size);margin-bottom:var(--size);-ms-flex-align:start;align-items:start;justify-items:center;gap:5px;display:grid}.gallery:has(:hover) picture:not(:hover){filter:brightness(.5)contrast(.5)}.gallery:has(:focus) picture:not(:focus){filter:brightness(.5)contrast(.5)}.gallery picture{object-fit:cover;width:calc(var(--size)*2);height:calc(var(--size)*2);clip-path:path("M90,10 C100,0 100,0 110,10 190,90 190,90 190,90 200,100 200,100 190,110 190,110 110,190 110,190 100,200 100,200 90,190 90,190 10,110 10,110 0,100 0,100 10,90Z");border-radius:5px;grid-column:auto/span 2;transition:clip-path .25s,filter .75s}.gallery picture:nth-child(5n-1){grid-column:2/span 2}.gallery picture:hover,.gallery picture:focus{clip-path:path("M0,0 C0,0 200,0 200,0 200,0 200,100 200,100 200,100 200,200 200,200 200,200 100,200 100,200 100,200 100,200 0,200 0,200 0,100 0,100 0,100 0,100 0,100Z");z-index:1;transition:clip-path .25s,filter .25s}.gallery picture:focus{outline-offset:-5px;outline:1px dashed #000}.carousel>input{clip:rect(1px,1px,1px,1px);clip-path:inset(50%);width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.carousel>input:nth-of-type(15):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1400%}.carousel>input:nth-of-type(14):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1300%}.carousel>input:nth-of-type(13):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1200%}.carousel>input:nth-of-type(12):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1100%}.carousel>input:nth-of-type(11):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-1000%}.carousel>input:nth-of-type(10):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-900%}.carousel>input:nth-of-type(9):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-800%}.carousel>input:nth-of-type(8):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-700%}.carousel>input:nth-of-type(7):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-600%}.carousel>input:nth-of-type(6):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-500%}.carousel>input:nth-of-type(5):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-400%}.carousel>input:nth-of-type(4):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-300%}.carousel>input:nth-of-type(3):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-200%}.carousel>input:nth-of-type(2):checked~.carousel__slides .carousel__slide:first-of-type{margin-left:-100%}.carousel>input:first-of-type:checked~.carousel__slides .carousel__slide:first-of-type{margin-left:0%}.carousel>input:first-of-type:checked~.carousel__thumbnails li:first-of-type,.carousel>input:nth-of-type(2):checked~.carousel__thumbnails li:nth-of-type(2),.carousel>input:nth-of-type(3):checked~.carousel__thumbnails li:nth-of-type(3),.carousel>input:nth-of-type(4):checked~.carousel__thumbnails li:nth-of-type(4),.carousel>input:nth-of-type(5):checked~.carousel__thumbnails li:nth-of-type(5),.carousel>input:nth-of-type(6):checked~.carousel__thumbnails li:nth-of-type(6),.carousel>input:nth-of-type(7):checked~.carousel__thumbnails li:nth-of-type(7),.carousel>input:nth-of-type(8):checked~.carousel__thumbnails li:nth-of-type(8),.carousel>input:nth-of-type(9):checked~.carousel__thumbnails li:nth-of-type(9),.carousel>input:nth-of-type(10):checked~.carousel__thumbnails li:nth-of-type(10),.carousel>input:nth-of-type(11):checked~.carousel__thumbnails li:nth-of-type(11),.carousel>input:nth-of-type(12):checked~.carousel__thumbnails li:nth-of-type(12),.carousel>input:nth-of-type(13):checked~.carousel__thumbnails li:nth-of-type(13),.carousel>input:nth-of-type(14):checked~.carousel__thumbnails li:nth-of-type(14),.carousel>input:nth-of-type(15):checked~.carousel__thumbnails li:nth-of-type(15){box-shadow:0 0 0 5px rgba(0,0,255,.5)}.carousel__slides{z-index:1;white-space:nowrap;box-sizing:border-box;margin:0;padding:0;display:-ms-flexbox;display:flex;position:relative;overflow:hidden}.carousel__slide{vertical-align:top;box-sizing:border-box;white-space:normal;-ms-flex:1 0 100%;flex:1 0 100%;width:100%;height:100%;transition:all .3s ease-out;display:block;position:relative;overflow:hidden}.carousel__slide figure{-ms-flex-direction:column;flex-direction:column;margin:0;display:-ms-flexbox;display:flex}.carousel__slide div{width:100%;position:relative}.carousel__slide div:before{content:"";width:100%;padding-top:66.6667%;display:block}.carousel__slide div>img{width:100%;height:100%;position:absolute;top:0;bottom:0;left:0;right:0}.carousel__slide img{object-fit:cover;-ms-flex:auto;flex:auto;display:block}.carousel__slide figcaption{-ms-flex:none;flex:none;-ms-flex-item-align:center;align-self:center;width:25%;min-width:150px;padding:20px 20px 0}.carousel__slide .credit{color:rgba(0,0,0,.5);margin-top:1rem;display:block}.carousel__slide.scrollable{overflow-y:scroll}.carousel__thumbnails{margin:0 -10px;padding:0;list-style:none;display:-ms-flexbox;display:flex}.carousel__slides+.carousel__thumbnails{margin-top:20px}.carousel__thumbnails li{-ms-flex:auto;flex:auto;max-width:calc(16.6667% - 20px);margin:0 10px;transition:all .3s ease-in-out}.carousel__thumbnails label{display:block;position:relative}.carousel__thumbnails label:before{content:"";width:100%;padding-top:100%;display:block}.carousel__thumbnails label>img{width:100%;height:100%;position:absolute;top:0;bottom:0;left:0;right:0}.carousel__thumbnails label:hover,.carousel__thumbnails label:focus{cursor:pointer}.carousel__thumbnails label:hover img,.carousel__thumbnails label:focus img{transition:all .3s ease-in-out;box-shadow:0 0 0 1px rgba(0,0,0,.25)}.carousel__thumbnails img{object-fit:cover;width:100%;height:100%;display:block}
\ No newline at end of file
diff --git a/docs/sitemap.xml b/docs/sitemap.xml
index e0c108df..511f5672 100644
--- a/docs/sitemap.xml
+++ b/docs/sitemap.xml
@@ -91,26 +91,6 @@
2025-12-03T21:10:08.000Z
-
- https://www.adamjolicoeur.com/designs/alm/
- 2026-01-01T16:25:50.000Z
-
-
-
- https://www.adamjolicoeur.com/designs/component-library/
- 2026-01-01T16:25:50.000Z
-
-
-
- https://www.adamjolicoeur.com/designs/task-it/
- 2026-01-01T16:25:50.000Z
-
-
-
- https://www.adamjolicoeur.com/
- 2026-01-05T16:14:23.050Z
-
-
https://www.adamjolicoeur.com/apps/taskstat-privacy.html2026-01-05T19:31:33.000Z
@@ -126,11 +106,6 @@
2026-01-19T16:58:02.000Z
-
- https://www.adamjolicoeur.com/development/archparser/
- 2026-01-19T16:58:02.000Z
-
-
https://www.adamjolicoeur.com/presentations/2026-01-19T16:58:02.000Z
@@ -176,4 +151,34 @@
2026-03-25T23:19:14.237Z
+
+ https://www.adamjolicoeur.com/case-studies/soccertracker/
+ 2026-03-26T01:32:09.000Z
+
+
+
+ https://www.adamjolicoeur.com/designs/alm/
+ 2026-03-26T01:47:27.000Z
+
+
+
+ https://www.adamjolicoeur.com/designs/component-library/
+ 2026-03-26T01:47:27.000Z
+
+
+
+ https://www.adamjolicoeur.com/designs/task-it/
+ 2026-03-26T01:47:27.000Z
+
+
+
+ https://www.adamjolicoeur.com/development/archparser/
+ 2026-03-26T01:47:27.000Z
+
+
+
+ https://www.adamjolicoeur.com/
+ 2026-03-26T01:59:11.541Z
+
+
diff --git a/src/assets/img-raw/soccergametracker-phone-history-details.png b/src/assets/img-raw/soccergametracker-phone-history-details.png
new file mode 100644
index 00000000..a74fedec
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-history-details.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-history.png b/src/assets/img-raw/soccergametracker-phone-history.png
new file mode 100644
index 00000000..86144b70
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-history.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-livegame.png b/src/assets/img-raw/soccergametracker-phone-livegame.png
new file mode 100644
index 00000000..22dea14a
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-livegame.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-newgame.png b/src/assets/img-raw/soccergametracker-phone-newgame.png
new file mode 100644
index 00000000..bda0c653
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-newgame.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-rosters.png b/src/assets/img-raw/soccergametracker-phone-rosters.png
new file mode 100644
index 00000000..3decd897
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-rosters.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-subs-01.png b/src/assets/img-raw/soccergametracker-phone-subs-01.png
new file mode 100644
index 00000000..585f5826
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-subs-01.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-subs-02.png b/src/assets/img-raw/soccergametracker-phone-subs-02.png
new file mode 100644
index 00000000..0a7baca3
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-subs-02.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-subs-03.png b/src/assets/img-raw/soccergametracker-phone-subs-03.png
new file mode 100644
index 00000000..7cebcdcf
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-subs-03.png differ
diff --git a/src/assets/img-raw/soccergametracker-phone-summary.png b/src/assets/img-raw/soccergametracker-phone-summary.png
new file mode 100644
index 00000000..356c6306
Binary files /dev/null and b/src/assets/img-raw/soccergametracker-phone-summary.png differ
diff --git a/src/assets/img/soccergametracker- ingamesummary-thumb.webp b/src/assets/img/soccergametracker- ingamesummary-thumb.webp
new file mode 100644
index 00000000..19855b1f
Binary files /dev/null and b/src/assets/img/soccergametracker- ingamesummary-thumb.webp differ
diff --git a/src/assets/img/soccergametracker- ingamesummary.webp b/src/assets/img/soccergametracker- ingamesummary.webp
new file mode 100644
index 00000000..a7ddbcf2
Binary files /dev/null and b/src/assets/img/soccergametracker- ingamesummary.webp differ
diff --git a/src/assets/img/soccergametracker-addnewplayer-thumb.webp b/src/assets/img/soccergametracker-addnewplayer-thumb.webp
new file mode 100644
index 00000000..2a4bccae
Binary files /dev/null and b/src/assets/img/soccergametracker-addnewplayer-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-addnewplayer.webp b/src/assets/img/soccergametracker-addnewplayer.webp
new file mode 100644
index 00000000..fb28296b
Binary files /dev/null and b/src/assets/img/soccergametracker-addnewplayer.webp differ
diff --git a/src/assets/img/soccergametracker-endgame-thumb.webp b/src/assets/img/soccergametracker-endgame-thumb.webp
new file mode 100644
index 00000000..7c3b068c
Binary files /dev/null and b/src/assets/img/soccergametracker-endgame-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-endgame.webp b/src/assets/img/soccergametracker-endgame.webp
new file mode 100644
index 00000000..bd5b9e71
Binary files /dev/null and b/src/assets/img/soccergametracker-endgame.webp differ
diff --git a/src/assets/img/soccergametracker-firsthalfsummary-thumb.webp b/src/assets/img/soccergametracker-firsthalfsummary-thumb.webp
new file mode 100644
index 00000000..279a924f
Binary files /dev/null and b/src/assets/img/soccergametracker-firsthalfsummary-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-firsthalfsummary.webp b/src/assets/img/soccergametracker-firsthalfsummary.webp
new file mode 100644
index 00000000..46483408
Binary files /dev/null and b/src/assets/img/soccergametracker-firsthalfsummary.webp differ
diff --git a/src/assets/img/soccergametracker-gamedetails-thumb.webp b/src/assets/img/soccergametracker-gamedetails-thumb.webp
new file mode 100644
index 00000000..25175eee
Binary files /dev/null and b/src/assets/img/soccergametracker-gamedetails-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-gamedetails.webp b/src/assets/img/soccergametracker-gamedetails.webp
new file mode 100644
index 00000000..cff54486
Binary files /dev/null and b/src/assets/img/soccergametracker-gamedetails.webp differ
diff --git a/src/assets/img/soccergametracker-goalassignment-thumb.webp b/src/assets/img/soccergametracker-goalassignment-thumb.webp
new file mode 100644
index 00000000..30427662
Binary files /dev/null and b/src/assets/img/soccergametracker-goalassignment-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-goalassignment.webp b/src/assets/img/soccergametracker-goalassignment.webp
new file mode 100644
index 00000000..f701132f
Binary files /dev/null and b/src/assets/img/soccergametracker-goalassignment.webp differ
diff --git a/src/assets/img/soccergametracker-halftime-thumb.webp b/src/assets/img/soccergametracker-halftime-thumb.webp
new file mode 100644
index 00000000..2461ba19
Binary files /dev/null and b/src/assets/img/soccergametracker-halftime-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-halftime.webp b/src/assets/img/soccergametracker-halftime.webp
new file mode 100644
index 00000000..ece26585
Binary files /dev/null and b/src/assets/img/soccergametracker-halftime.webp differ
diff --git a/src/assets/img/soccergametracker-history-thumb.webp b/src/assets/img/soccergametracker-history-thumb.webp
new file mode 100644
index 00000000..ee820d4c
Binary files /dev/null and b/src/assets/img/soccergametracker-history-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-history.webp b/src/assets/img/soccergametracker-history.webp
new file mode 100644
index 00000000..978f9963
Binary files /dev/null and b/src/assets/img/soccergametracker-history.webp differ
diff --git a/src/assets/img/soccergametracker-history02-thumb.webp b/src/assets/img/soccergametracker-history02-thumb.webp
new file mode 100644
index 00000000..0c38bfa6
Binary files /dev/null and b/src/assets/img/soccergametracker-history02-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-history02.webp b/src/assets/img/soccergametracker-history02.webp
new file mode 100644
index 00000000..59d7880a
Binary files /dev/null and b/src/assets/img/soccergametracker-history02.webp differ
diff --git a/src/assets/img/soccergametracker-newgame-thumb.webp b/src/assets/img/soccergametracker-newgame-thumb.webp
new file mode 100644
index 00000000..c74c2fc1
Binary files /dev/null and b/src/assets/img/soccergametracker-newgame-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-newgame.webp b/src/assets/img/soccergametracker-newgame.webp
new file mode 100644
index 00000000..0d11e530
Binary files /dev/null and b/src/assets/img/soccergametracker-newgame.webp differ
diff --git a/src/assets/img/soccergametracker-phone-history-details-thumb.webp b/src/assets/img/soccergametracker-phone-history-details-thumb.webp
new file mode 100644
index 00000000..3ef0a78a
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-history-details-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-history-details.webp b/src/assets/img/soccergametracker-phone-history-details.webp
new file mode 100644
index 00000000..4d4d9806
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-history-details.webp differ
diff --git a/src/assets/img/soccergametracker-phone-history-thumb.webp b/src/assets/img/soccergametracker-phone-history-thumb.webp
new file mode 100644
index 00000000..98b6da6f
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-history-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-history.webp b/src/assets/img/soccergametracker-phone-history.webp
new file mode 100644
index 00000000..24fb65ea
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-history.webp differ
diff --git a/src/assets/img/soccergametracker-phone-livegame-thumb.webp b/src/assets/img/soccergametracker-phone-livegame-thumb.webp
new file mode 100644
index 00000000..1f2082a6
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-livegame-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-livegame.webp b/src/assets/img/soccergametracker-phone-livegame.webp
new file mode 100644
index 00000000..8ce97c47
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-livegame.webp differ
diff --git a/src/assets/img/soccergametracker-phone-newgame-thumb.webp b/src/assets/img/soccergametracker-phone-newgame-thumb.webp
new file mode 100644
index 00000000..378d04aa
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-newgame-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-newgame.webp b/src/assets/img/soccergametracker-phone-newgame.webp
new file mode 100644
index 00000000..60d102a0
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-newgame.webp differ
diff --git a/src/assets/img/soccergametracker-phone-rosters-thumb.webp b/src/assets/img/soccergametracker-phone-rosters-thumb.webp
new file mode 100644
index 00000000..91f4ff6e
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-rosters-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-rosters.webp b/src/assets/img/soccergametracker-phone-rosters.webp
new file mode 100644
index 00000000..b6114b9f
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-rosters.webp differ
diff --git a/src/assets/img/soccergametracker-phone-subs-01-thumb.webp b/src/assets/img/soccergametracker-phone-subs-01-thumb.webp
new file mode 100644
index 00000000..33e2756d
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-subs-01-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-subs-01.webp b/src/assets/img/soccergametracker-phone-subs-01.webp
new file mode 100644
index 00000000..034ff885
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-subs-01.webp differ
diff --git a/src/assets/img/soccergametracker-phone-subs-02-thumb.webp b/src/assets/img/soccergametracker-phone-subs-02-thumb.webp
new file mode 100644
index 00000000..7eca5a55
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-subs-02-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-subs-02.webp b/src/assets/img/soccergametracker-phone-subs-02.webp
new file mode 100644
index 00000000..7b5ef5bf
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-subs-02.webp differ
diff --git a/src/assets/img/soccergametracker-phone-subs-03-thumb.webp b/src/assets/img/soccergametracker-phone-subs-03-thumb.webp
new file mode 100644
index 00000000..0f1b4293
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-subs-03-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-subs-03.webp b/src/assets/img/soccergametracker-phone-subs-03.webp
new file mode 100644
index 00000000..6984bfcc
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-subs-03.webp differ
diff --git a/src/assets/img/soccergametracker-phone-summary-thumb.webp b/src/assets/img/soccergametracker-phone-summary-thumb.webp
new file mode 100644
index 00000000..d8640397
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-summary-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-phone-summary.webp b/src/assets/img/soccergametracker-phone-summary.webp
new file mode 100644
index 00000000..e88f2493
Binary files /dev/null and b/src/assets/img/soccergametracker-phone-summary.webp differ
diff --git a/src/assets/img/soccergametracker-playerstats01-thumb.webp b/src/assets/img/soccergametracker-playerstats01-thumb.webp
new file mode 100644
index 00000000..5e726829
Binary files /dev/null and b/src/assets/img/soccergametracker-playerstats01-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-playerstats01.webp b/src/assets/img/soccergametracker-playerstats01.webp
new file mode 100644
index 00000000..0bc84313
Binary files /dev/null and b/src/assets/img/soccergametracker-playerstats01.webp differ
diff --git a/src/assets/img/soccergametracker-playerstats02-thumb.webp b/src/assets/img/soccergametracker-playerstats02-thumb.webp
new file mode 100644
index 00000000..f4109931
Binary files /dev/null and b/src/assets/img/soccergametracker-playerstats02-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-playerstats02.webp b/src/assets/img/soccergametracker-playerstats02.webp
new file mode 100644
index 00000000..64670a00
Binary files /dev/null and b/src/assets/img/soccergametracker-playerstats02.webp differ
diff --git a/src/assets/img/soccergametracker-roster-thumb.webp b/src/assets/img/soccergametracker-roster-thumb.webp
new file mode 100644
index 00000000..f4005e62
Binary files /dev/null and b/src/assets/img/soccergametracker-roster-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-roster.webp b/src/assets/img/soccergametracker-roster.webp
new file mode 100644
index 00000000..be270655
Binary files /dev/null and b/src/assets/img/soccergametracker-roster.webp differ
diff --git a/src/assets/img/soccergametracker-secondhalfsummary-thumb.webp b/src/assets/img/soccergametracker-secondhalfsummary-thumb.webp
new file mode 100644
index 00000000..eda55618
Binary files /dev/null and b/src/assets/img/soccergametracker-secondhalfsummary-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-secondhalfsummary.webp b/src/assets/img/soccergametracker-secondhalfsummary.webp
new file mode 100644
index 00000000..f2a646d8
Binary files /dev/null and b/src/assets/img/soccergametracker-secondhalfsummary.webp differ
diff --git a/src/assets/img/soccergametracker-startgame-thumb.webp b/src/assets/img/soccergametracker-startgame-thumb.webp
new file mode 100644
index 00000000..e8a9811e
Binary files /dev/null and b/src/assets/img/soccergametracker-startgame-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-startgame.webp b/src/assets/img/soccergametracker-startgame.webp
new file mode 100644
index 00000000..b40413f3
Binary files /dev/null and b/src/assets/img/soccergametracker-startgame.webp differ
diff --git a/src/assets/img/soccergametracker-substituteprompt-thumb.webp b/src/assets/img/soccergametracker-substituteprompt-thumb.webp
new file mode 100644
index 00000000..c1cbb5fa
Binary files /dev/null and b/src/assets/img/soccergametracker-substituteprompt-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-substituteprompt.webp b/src/assets/img/soccergametracker-substituteprompt.webp
new file mode 100644
index 00000000..07bb853c
Binary files /dev/null and b/src/assets/img/soccergametracker-substituteprompt.webp differ
diff --git a/src/assets/img/soccergametracker-substitutionsummary-thumb.webp b/src/assets/img/soccergametracker-substitutionsummary-thumb.webp
new file mode 100644
index 00000000..4fdeb8be
Binary files /dev/null and b/src/assets/img/soccergametracker-substitutionsummary-thumb.webp differ
diff --git a/src/assets/img/soccergametracker-substitutionsummary.webp b/src/assets/img/soccergametracker-substitutionsummary.webp
new file mode 100644
index 00000000..6134b98f
Binary files /dev/null and b/src/assets/img/soccergametracker-substitutionsummary.webp differ
diff --git a/src/pages/case-studies/case-studies.json b/src/pages/case-studies/case-studies.json
new file mode 100644
index 00000000..2dd05d0b
--- /dev/null
+++ b/src/pages/case-studies/case-studies.json
@@ -0,0 +1,4 @@
+{
+ "layout": "page",
+ "permalink": "/case-studies/{{ page.fileSlug }}/"
+}
diff --git a/src/pages/case-studies/soccertracker.md b/src/pages/case-studies/soccertracker.md
new file mode 100644
index 00000000..003b7b70
--- /dev/null
+++ b/src/pages/case-studies/soccertracker.md
@@ -0,0 +1,212 @@
+---
+layout: markdown.njk
+title: 'Soccer Game Tracker'
+date: git Last Modified
+abbreviation: 'soccertracker'
+description: 'Native iOS app for coaches to track game data. Solo project, in active use.'
+eleventyNavigation:
+ key: CaseStudies
+ parent: CaseStudies
+ order: 2
+containers: true
+---
+
+:::section
+
+ ## Project Overview
+
+ > A native iOS app, built by a coach, who needed fast and reliable game tracking without surrendering data to a third-party platform — designed for the sideline, built in Swift.
+
+ :::card
+
+ **ROLE**: Designer & Developer
+
+ **STACK**: SwiftUI
+
+ **STATUS**: Work in progress
+
+:::
+
+::::section
+
+## Introduction
+
+ Coaching a youth soccer team generates a surprising amount of data. Scores, substitutions, player minutes, goals, assists, saves, cards. The problem isn't that the data is hard to capture — it's that every existing option forces a tradeoff I wasn't willing to make. Pen and paper is fast but produces nothing useful afterward. Third-party apps are convenient but own your data, require accounts, and are built for organizations, not individual coaches. I wanted something purpose-built for how I actually work on a sideline: fast to set up, minimal interaction during play, and with data I control entirely.
+
+
+
+
+
+
+ set the roster - starters and substitutes
+
+
+
+
+
+
+
+ add game info - team size, location, date & time
+
+
+
+
+::::
+
+::::section
+## The harder right choice
+
+ I built SoccerGameTracker as a native iOS app in Swift — solo, from scratch. The easier path would have been a PWA or React Native wrapper; I've shipped both, and they're faster to build. I chose Swift because a sideline app has a specific performance contract: launch instantly, respond without lag when a goal gets scored, never drop data if the phone gets pocketed. Native with local storage and no network dependency satisfies all three. No sync to worry about, no account to authenticate, no API that might be down at kickoff.
+
+ > every decision went through one person - me
+
+ **Constraint clarified scope**. Without a backend, multi-device sync and shared rosters aren't on the table — and that's fine. Knowing what the app isn't made it easier to build what it is. I handled both the design and implementation entirely, which meant every decision — from interaction model to data structure — went through one person. That's mostly an advantage when you need to move fast and stay opinionated.
+::::
+
+::::section
+
+ ## Designing for the sideline
+
+ > The core design problem is attention.
+
+ During a live game, a coach is watching players, calling substitutions, talking to parents. The app gets a glance and a thumb tap, not focused interaction. Every decision on the Live Game screen follows from that.
+
+
+
Pregame
+
+ The pre-game setup flow handles the overhead up front.
+ New Game collects opposition, date, location, and half duration — everything you'd put on a physical scoresheet — plus roster selection from a pre-built player list with jersey number and position.
+ The Game vs. Scrimmage toggle is a small but deliberate decision: scrimmages have different stakes and different lineup dynamics, and I wanted the history to reflect that distinction clearly.
+
+
+
+
+
Scoreboard & Quick Actions
+
+ The scoreboard and half timer sit at the top, readable in a second. The Quick Actions section — Team Goal and Opponent Goal — are the two largest tap targets in the app, sized and colored for immediate recognition in any lighting. Correction actions (Remove Team Goal, Subtract Goal) are present but visually subordinate: same area, smaller, lighter. Reachable when you need them, harder to hit by accident.
+
+
+
+
+
Players & Activity
+
+ Below the quick actions is where the app earns its depth. Each player in the active roster is tappable, opening a detail view where you can log individual stats in real time: goals, assists, saves, shots, yellow cards, red cards. Every action is timestamped and tied to the game half — building a full action log that the post-game summary renders as a timeline. That's the data that makes post-game review actually useful: not just who won, but who contributed, when, and how often.
+
+
+
+
+
+
+
+ Live Game screen showing scoreboard, timer, Quick Actions, and player list
+
+
+
+
+
+ ### Substitutions
+
+ :::card
+ Substitutions were a meaningful product decision. Mid-game sub support — with a distinct "substituted out" state separate from bench status — reflects how substitutions actually work: a player who comes off isn't the same as a player who never started. Getting that distinction right matters for per-player stats to be accurate.
+ :::
+
+
+
+
+
+
+ View a player's stats and position before substituting them on or off
+
+
+
+
+ View who subbed in and out of the game by half
+
+
+
+
+::::
+
+::::section
+
+ ## After the game
+
+ > As a coach, being able to view historical information is a key part in planning practices, setting lineups, and determining strategy.
+
+ ### History
+
+
+
+
+
+
+ View historical game data, with key info front and center
+
+
+
+
+
+
+
+ Zooming into any game shows player-by-player stats
+
+
+
+
+::::
+
+::::section
+ ## Where it stands
+
+ :::card
+ The app is in active use this season, tested against the real constraint it was built for: a sideline, in real game conditions. Feedback from that use has been indispensable, with the per-player stats used between games to inform lineup and substitution planning. That's the outcome I was building toward.
+
+ What real use has surfaced is mostly sequencing: the transition from pre-game setup into the live view needs less friction, and the post-game summary screen is still being refined. **Those are the next iterations.**
+ :::
+
+ > An App Store release is planned once the final rough edges are resolved. The app does one thing, does it reliably, and doesn't ask for anything it doesn't need.
+::::
diff --git a/src/sass/_layout.scss b/src/sass/_layout.scss
index 72ea329a..e1f91f17 100644
--- a/src/sass/_layout.scss
+++ b/src/sass/_layout.scss
@@ -15,6 +15,9 @@ section {
.justify-content-between {
justify-content: space-between !important;
}
+.justify-content-around {
+ justify-content: space-around !important;
+}
.justify-content-end {
justify-content: flex-end !important;
}