Total steps
+0
+Ranked —
+diff --git a/LICENSE.md b/LICENSE.md index 9965b0865..1673a6c0c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,3 @@ MIT License -Copyright (c) 2019 Jacobo Martínez - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Copyright (c) 2023 Benjamin Woolston diff --git a/README.md b/README.md index 2028d8528..4854e7c69 100644 --- a/README.md +++ b/README.md @@ -1,360 +1,27 @@ -# Simplefolio ⚡️ [](https://github.com/cobiwave/simplefolio/blob/master/LICENSE.md)   +# Benjamin Woolston — Portfolio & Extras -## A minimal portfolio template for Developers! +This repo powers the portfolio as well as small side pages that live on subpaths. -
-
-- - Know more - -
-` tag with class name `.about-wrapper__info-text`, include information about you, I recommend to put 2 paragraphs in order to work well and a maximum of 3 paragraphs.
-- On last `` tag, include your CV (.pdf) path on `href` property, your resume CV must be located inside `/src/assets/` folder.
-
-```html
-
-
- This is where you can describe about yourself. The more you describe
- about yourself, the more chances you can!
-
- Extra Information about you! like hobbies and your goals.
- ` tag with `loremp ipsum` text, include your project description.
-- On first `` tag, put your project url on `href` property.
-- On second `` tag, put your project repository url on `href` property.
-
----
-
-- Inside `
- Lorem ipsum dolor sit, amet consectetur adipisicing elit. Excepturi
- neque, ipsa animi maiores repellendus distinctio aperiam earum dolor
- voluptatum consequatur blanditiis inventore debitis fuga numquam
- voluptate ex architecto itaque molestiae.
- ` tag with class name `.contact-wrapper__text`, include some custom call-to-action message.
-- On `` tag, put your email address on `href` property.
-
-```html
-
- [Put your call to action here]
- Know more
-
- This is where you can describe about yourself. The more you
- describe about yourself, the more chances you have!
-
- Extra Information about you! like hobbies and your goals.
-
- Describe the project being very specific, you can use the Twitter standard: no more than 280 characters:
- complement the information: the skills learned or reinforced in its realization and how you faced it,
- prove to be proactive in the search for solutions.
-
- Demonstrate in this description the skills of a programmer: such as having commitment,
- having perseverance and accepting alternative solutions. Remember that being a portfolio you are not selling the project,
- you are selling yourself, it reflects the resources used: Frameworks, libraries, platforms, etc.
-
- If the project was collaborative, reflect it in this description, that will demonstrate communication and/or leadership skills.
- Additionally, if you made use of the mastery of a second language, it will reflect on you professionalism.
- [Put your call to action here] I am a Senior Engineer at Telstra. I deploy fast, scalable solutions like RCS messaging. I grew up surrounded by hacking, ebikes, drones and videogames. At 18, I decided I'd had enough of the games and left to study Electrical Engineering and Finance at the University of Queensland in Brisbane. Now I work with software. I like to find flow states wherever I can - I lift weights, play sports (tennis, pickleball, squash), competitively race drones, meditate, run, and am trying (mainly struggling) to learn to play guitar. I'm starting to track the books I read here. Selected things I’ve built to give people a little more leverage.
+ I sent an email to every startup company in the world asking for a free T-Shirt.
+ At Swyftx, my manager wanted me to increase team efficiency, so I created Python macros in my free time to speed up my workflow and ended up making my team 10% more efficient, bringing us to the #1/14 position in the team efficiency chart.
+
+ I started my first business at 15, and I'm now the owner of the business Cannology. We are now sold in over 50 pharmacies across Australia. Redirecting to woolston.dev/ww/… 🏆 Third Annual Wooly Walking Challenge
+ The family step-off returns from 6 October to 21 December 2025.
+ Log your steps, track the leaderboard, and keep your totals secret once the stealth phase begins.
+ Use your Wooly Walking username. Passwords start as You made it. Stay moving, stay legendary. 0 Ranked — 0 — 0 Consecutive active days — Unlock badges by logging steps. Track how your weekly totals stack up against the crown pace. Personal trend Totals refresh whenever someone logs their steps. Enter your steps for each day. Totals update instantly. Download your local data or import an updated file from another device.About me
-
- ` tag with class name `.project-wrapper__text-title`, include your project title.
-- On `
` and put again your project url in the `href` property of the `` tag.
-- Recommended size for project image (1366 x 767), your project image must be located inside `/src/assets/` folder.
-
-```html
-
-
Project Title
-
- Contact
-
- Hi, my name is Your Name
-
-
- I'm the Unknown Developer.
- About me
-
- Projects
-
-
- Project Title 0
-
- Project Title 1
-
- Project Title 2
-
- Contact
- Ben Woolston
+
+ Projects
+
+
+
+ T-Shirt Extravaganza
+
+ To do this I used several data scraping tools like Scrapy & Selenium, as well as Google Bard Exploitation with fifty Google accounts to find company names and emails of over 5000 startups. The results are I received over 90 T-Shirts, 10 Water Bottles, 17 booklets and hundreds of stickers!
+ At peak, I hit Bard with >1500 requests a minute.
+
+
+ Swyftx: Boosting Team Efficiency
+
+
+ Cannology
+
+ The business takes up a small part of each week and is something I love building in the background. I’m currently emailing every chemist in Australia to become a stockist, all automated with Python & ChatGPT. Visit Website.
+
+
+
+
diff --git a/src/sass/abstracts/_variables.scss b/src/sass/abstracts/_variables.scss
index 6cbe5c3f1..df995b332 100755
--- a/src/sass/abstracts/_variables.scss
+++ b/src/sass/abstracts/_variables.scss
@@ -1,6 +1,6 @@
// COLORS
-$primary-color: #02aab0;
-$secondary-color: #00cdac;
+$primary-color: #4493c5;
+$secondary-color: #44A2BC;
$white-color: #fff;
diff --git a/src/w/index.html b/src/w/index.html
new file mode 100644
index 000000000..74ecad168
--- /dev/null
+++ b/src/w/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+ Walk proud. Walk hard. Claim the crown.
+ Family login
+ Password1.Welcome, Walker!
+ Momentum
+ Total steps
+ Best day
+ Streak
+ Achievement
+ Weekly momentum
+ Tracking weekly output
+ Leaderboard
+
+
+
+
+
+
+
+ Rank
+ Walker
+ Total steps
+ Weekly pace
+ Log your steps
+ Backup & restore
+ #${index + 1}
+ ${record.icon} ${record.name}
+ ${totalCellContent}
+ ${paceValue}
+ `;
+
+ leaderboardBody.appendChild(row);
+ });
+}
+
+function renderWeeks() {
+ weeksContainer.innerHTML = "";
+ const userData = appState.data.participants[appState.activeUser];
+ const todayIso = dateToIso(new Date());
+
+ weeks.forEach((week, index) => {
+ const card = document.createElement("article");
+ card.className = "week-card";
+ if (index === 0) {
+ card.classList.add("is-open");
+ }
+
+ const weekTotal = week.dates.reduce((sum, day) => sum + (userData.dailySteps[day.iso] || 0), 0);
+
+ const header = document.createElement("div");
+ header.className = "week-card__header";
+ header.innerHTML = `
+ Week ${index + 1}
+ ${week.label}
+
+
+
+
+
+ Day
+ Steps
+
+
+
+ `;
+
+ const tbody = table.querySelector("tbody");
+
+ week.dates.forEach((day) => {
+ const tr = document.createElement("tr");
+
+ const label = document.createElement("td");
+ label.className = "week-card__day-label";
+ const isToday = day.iso === todayIso;
+ if (isToday) {
+ label.classList.add("is-today");
+ }
+ label.innerHTML = `${day.short}${day.long}`;
+
+ const inputCell = document.createElement("td");
+ const input = document.createElement("input");
+ input.type = "number";
+ input.min = "0";
+ input.step = "1";
+ input.inputMode = "numeric";
+ input.value = userData.dailySteps[day.iso] || "";
+ input.dataset.date = day.iso;
+ input.dataset.weekIndex = String(index);
+
+ input.addEventListener("change", (event) => {
+ const raw = event.target.value;
+ const parsed = Math.max(0, parseInt(raw, 10) || 0);
+ event.target.value = parsed ? String(parsed) : "";
+ userData.dailySteps[day.iso] = parsed;
+ saveData(appState.data);
+ updateWeekTotals(index);
+ renderMomentumCards();
+ renderLeaderboard(resolvePhase(new Date()));
+ renderWeeklyChart();
+ });
+
+ inputCell.appendChild(input);
+ tr.appendChild(label);
+ tr.appendChild(inputCell);
+ tbody.appendChild(tr);
+ });
+
+ body.appendChild(table);
+ card.appendChild(header);
+ card.appendChild(body);
+ weeksContainer.appendChild(card);
+ });
+}
+
+function updateWeekTotals(weekIndex) {
+ const userData = appState.data.participants[appState.activeUser];
+ const week = weeks[weekIndex];
+ const total = week.dates.reduce((sum, day) => sum + (userData.dailySteps[day.iso] || 0), 0);
+ const badge = document.querySelector(`[data-week-total="${weekIndex}"]`);
+ const footer = document.querySelector(`[data-week-total-footer="${weekIndex}"]`);
+ const label = `${formatNumber(total)} steps`;
+ if (badge) badge.textContent = label;
+ if (footer) footer.textContent = label;
+}
+
+function computeTotals(dailySteps) {
+ let totalSteps = 0;
+ let bestDaySteps = 0;
+ let bestDayIso = "";
+ let currentStreak = 0;
+ let streak = 0;
+
+ const today = new Date();
+ const todayIso = dateToIso(today);
+
+ const days = challengeDates.map((day) => ({ ...day, steps: dailySteps[day.iso] || 0 }));
+
+ days.forEach((day) => {
+ totalSteps += day.steps;
+ if (day.steps > bestDaySteps) {
+ bestDaySteps = day.steps;
+ bestDayIso = day.iso;
+ }
+ });
+
+ for (let i = days.length - 1; i >= 0; i -= 1) {
+ const day = days[i];
+ if (day.steps > 0) {
+ streak += 1;
+ currentStreak = streak;
+ } else {
+ if (dateIsAfterIso(todayIso, day.iso)) {
+ break;
+ }
+ streak = 0;
+ }
+ }
+
+ const completedDays = days.filter((day) => dateIsAfterIso(todayIso, day.iso) || day.iso === todayIso).length;
+ const weeksElapsed = Math.max(1, completedDays / 7);
+
+ return {
+ totalSteps,
+ bestDaySteps,
+ bestDayLabel: bestDayIso ? readableDate(bestDayIso) : "",
+ currentStreak,
+ weeklyAverage: totalSteps / weeksElapsed
+ };
+}
+
+function computeRank(username) {
+ const everyone = PARTICIPANTS.map((person) => {
+ const totals = computeTotals(appState.data.participants[person.username].dailySteps);
+ return { username: person.username, total: totals.totalSteps };
+ }).sort((a, b) => b.total - a.total);
+
+ const position = Math.max(1, everyone.findIndex((entry) => entry.username === username) + 1);
+ return { position, total: everyone.length };
+}
+
+function resolveBadge(totals) {
+ if (totals.totalSteps >= 420000) {
+ return { title: "Crown Chaser", caption: "Walking royalty in the making." };
+ }
+ if (totals.currentStreak >= 14) {
+ return { title: "Consistency Beast", caption: "14+ day streak — unstoppable." };
+ }
+ if (totals.bestDaySteps >= 25000) {
+ return { title: "Power Surge", caption: "One monster day above 25k." };
+ }
+ if (totals.totalSteps >= 210000) {
+ return { title: "Halfway Hero", caption: "You passed the halfway mark." };
+ }
+ if (totals.bestDaySteps >= 15000) {
+ return { title: "Sprinter", caption: "Huge daily burst logged." };
+ }
+ if (totals.totalSteps >= 70000) {
+ return { title: "On the Board", caption: "Seven days of 10k pace." };
+ }
+ return { title: "Keep marching", caption: "Log steps to unlock your first badge." };
+}
+
+function resolvePhase(now) {
+ const beforeStart = now < CHALLENGE.start;
+ const afterEnd = now > CHALLENGE.end;
+ const inStealth = now >= CHALLENGE.stealthStart && now <= CHALLENGE.end;
+
+ if (beforeStart) {
+ const days = Math.ceil((CHALLENGE.start - now) / (1000 * 60 * 60 * 24));
+ return {
+ label: "Warm-up",
+ message: "Prep those calves. Countdown to the starting gun.",
+ leaderboardCopy: "Warm-up period. Totals will appear once the challenge kicks off.",
+ daysRemaining: Math.max(days, 0),
+ stealth: false,
+ revealed: false
+ };
+ }
+
+ if (afterEnd) {
+ return {
+ label: "Grand reveal",
+ message: "Time to crown the champion and grill the bottom two chefs.",
+ leaderboardCopy: "Final results unlocked. Congratulate (or heckle) accordingly.",
+ daysRemaining: 0,
+ stealth: false,
+ revealed: true
+ };
+ }
+
+ if (inStealth) {
+ const days = Math.ceil((CHALLENGE.end - now) / (1000 * 60 * 60 * 24));
+ return {
+ label: "Stealth mode",
+ message: "Totals are hidden. Keep logging and keep them guessing.",
+ leaderboardCopy: "Stealth mode active — only your own totals are visible.",
+ daysRemaining: Math.max(days, 0),
+ stealth: true,
+ revealed: false
+ };
+ }
+
+ const days = Math.ceil((CHALLENGE.end - now) / (1000 * 60 * 60 * 24));
+ return {
+ label: "Active battle",
+ message: "Clock those steps weekly. Top spot is there for the taking.",
+ leaderboardCopy: "Live totals update whenever someone logs their steps.",
+ daysRemaining: Math.max(days, 0),
+ stealth: false,
+ revealed: false
+ };
+}
+
+function buildChallengeDates(start, end) {
+ const dates = [];
+ const cursor = new Date(start);
+ while (cursor <= end) {
+ const iso = dateToIso(cursor);
+ dates.push({
+ iso,
+ label: readableDate(iso),
+ short: cursor.toLocaleDateString(undefined, { weekday: "short" }),
+ long: cursor.toLocaleDateString(undefined, { month: "short", day: "numeric" })
+ });
+ cursor.setDate(cursor.getDate() + 1);
+ }
+ return dates;
+}
+
+function chunkDatesByWeek(dates) {
+ const chunks = [];
+ for (let i = 0; i < dates.length; i += 7) {
+ const slice = dates.slice(i, i + 7);
+ const first = slice[0];
+ const last = slice[slice.length - 1];
+ const label = `${first.long} – ${last.long}`;
+ chunks.push({ dates: slice, label });
+ }
+ return chunks;
+}
+
+function formatNumber(value) {
+ return new Intl.NumberFormat().format(value);
+}
+
+function dateToIso(date) {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0");
+ const day = String(date.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+}
+
+function readableDate(isoString) {
+ const date = new Date(isoString);
+ return date.toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
+}
+
+function dateIsAfterIso(referenceIso, targetIso) {
+ return referenceIso > targetIso;
+}
+
+function showAuthError(message) {
+ authError.textContent = message;
+}
+
+function clearAuthError() {
+ authError.textContent = "";
+}
+
+function syncImportedData(parsed) {
+ const normalised = normaliseData(parsed);
+ appState.data = normalised;
+ if (appState.activeUser && !appState.data.participants[appState.activeUser]) {
+ appState.activeUser = null;
+ dashboard.hidden = true;
+ authPanel.hidden = false;
+ }
+}
+
+function initialiseTheme() {
+ let stored = null;
+ try {
+ stored = window.localStorage.getItem(THEME_KEY);
+ } catch (error) {
+ stored = null;
+ }
+ let theme = stored === "light" || stored === "dark" ? stored : null;
+ if (!theme) {
+ const prefersLight = window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches;
+ theme = prefersLight ? "light" : "dark";
+ }
+ applyTheme(theme);
+}
+
+function applyTheme(theme) {
+ document.body.setAttribute("data-theme", theme);
+ try {
+ window.localStorage.setItem(THEME_KEY, theme);
+ } catch (error) {
+ // ignore storage issues
+ }
+ updateThemeToggle(theme);
+ if (appState.activeUser) {
+ renderWeeklyChart();
+ }
+}
+
+function updateThemeToggle(theme) {
+ if (!themeToggle) return;
+ themeToggle.dataset.state = theme;
+ themeToggle.setAttribute("aria-label", theme === "dark" ? "Switch to light mode" : "Switch to dark mode");
+ themeToggle.setAttribute("aria-checked", theme === "light" ? "true" : "false");
+ if (themeToggleLabel) {
+ themeToggleLabel.textContent = theme === "dark" ? "Dark mode" : "Light mode";
+ }
+}
+
+function renderWeeklyChart() {
+ if (!appState.activeUser || !progressChartCanvas || typeof Chart === "undefined") {
+ return;
+ }
+
+ const ctx = progressChartCanvas.getContext("2d");
+ if (!ctx) return;
+
+ const computed = window.getComputedStyle(document.body);
+ const accent = computed.getPropertyValue("--accent").trim() || "#ff6a3d";
+ const accentSoft = computed.getPropertyValue("--accent-soft").trim() || "rgba(255, 106, 61, 0.2)";
+ const success = computed.getPropertyValue("--success").trim() || "#60f5d2";
+ const textMuted = computed.getPropertyValue("--text-muted").trim() || "#a1a1aa";
+ const textPrimary = computed.getPropertyValue("--text-primary").trim() || "#111827";
+ const chartGrid = computed.getPropertyValue("--chart-grid").trim() || "rgba(160, 174, 192, 0.25)";
+ const tooltipBg = computed.getPropertyValue("--chart-tooltip-bg").trim() || "rgba(8, 10, 26, 0.94)";
+ const tooltipBorder = computed.getPropertyValue("--chart-tooltip-border").trim() || "rgba(255, 255, 255, 0.12)";
+ const borderSoft = computed.getPropertyValue("--border-soft").trim() || "rgba(255, 255, 255, 0.08)";
+
+ const userData = appState.data.participants[appState.activeUser];
+ const weekTotals = weeks.map((week) => week.dates.reduce((sum, day) => sum + (userData.dailySteps[day.iso] || 0), 0));
+ const labels = weeks.map((_, index) => `W${index + 1}`);
+ const todayIso = dateToIso(new Date());
+ const challengeStartIso = challengeDates[0]?.iso || todayIso;
+
+ // Show weeks that have started (first day of week is today or earlier)
+ const revealedWeeks = weeks.map((week) => {
+ const weekStartIso = week.dates[0].iso;
+ return weekStartIso <= todayIso;
+ });
+
+ const showAllWeeks = todayIso < challengeStartIso;
+ const totalsForChart = weekTotals.map((total, index) => (showAllWeeks || revealedWeeks[index] ? total : null));
+ const goalData = weekTotals.map((_, index) => (showAllWeeks || revealedWeeks[index] ? WEEKLY_GOAL : null));
+
+ const canvasHeight = progressChartCanvas.offsetHeight || progressChartCanvas.height || 260;
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
+ gradient.addColorStop(0, accent);
+ gradient.addColorStop(1, accentSoft || accent);
+
+ if (progressChart) {
+ progressChart.destroy();
+ }
+
+ progressChart = new Chart(ctx, {
+ type: "bar",
+ data: {
+ labels,
+ datasets: [
+ {
+ type: "bar",
+ label: "Your steps",
+ data: totalsForChart,
+ backgroundColor: gradient,
+ borderRadius: 14,
+ borderSkipped: false,
+ maxBarThickness: 42
+ },
+ {
+ type: "line",
+ label: "Goal pace",
+ data: goalData,
+ borderColor: success,
+ borderWidth: 2,
+ borderDash: [6, 6],
+ pointBackgroundColor: success,
+ pointBorderColor: success,
+ pointRadius: 0,
+ pointHoverRadius: 4,
+ tension: 0.35
+ }
+ ]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ interaction: {
+ mode: "index",
+ intersect: false
+ },
+ plugins: {
+ legend: {
+ display: false
+ },
+ tooltip: {
+ backgroundColor: tooltipBg,
+ borderColor: tooltipBorder,
+ borderWidth: 1,
+ titleColor: textPrimary,
+ bodyColor: textPrimary,
+ displayColors: false,
+ callbacks: {
+ title(context) {
+ return context[0]?.label || "";
+ },
+ label(context) {
+ const value = context.parsed.y;
+ if (value === null || Number.isNaN(value)) return "";
+ return `${formatNumber(Math.round(value))} steps`;
+ }
+ }
+ }
+ },
+ layout: {
+ padding: {
+ top: 12,
+ right: 16,
+ left: 8,
+ bottom: 0
+ }
+ },
+ scales: {
+ x: {
+ grid: {
+ display: false
+ },
+ ticks: {
+ color: textMuted,
+ font: {
+ family: '"Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
+ weight: "600"
+ }
+ }
+ },
+ y: {
+ beginAtZero: true,
+ grid: {
+ color: chartGrid,
+ borderDash: [6, 6],
+ drawBorder: false
+ },
+ ticks: {
+ color: textMuted,
+ padding: 8,
+ font: {
+ family: '"Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
+ },
+ callback(value) {
+ const numeric = Number(value) || 0;
+ if (numeric >= 1000) {
+ return `${Math.round(numeric / 1000)}k`;
+ }
+ return formatNumber(numeric);
+ }
+ }
+ }
+ }
+ }
+ });
+}
+
+// Auto-render if a remembered user exists and the password form was bypassed earlier.
+if (lastUser && PARTICIPANTS.some((person) => person.username === lastUser)) {
+ setActiveUser(lastUser);
+ renderDashboard();
+}
diff --git a/src/ww/styles.scss b/src/ww/styles.scss
new file mode 100644
index 000000000..b8d1bbcd5
--- /dev/null
+++ b/src/ww/styles.scss
@@ -0,0 +1,994 @@
+:root {
+ --radius: 28px;
+}
+
+:root,
+body[data-theme="dark"] {
+ --bg-1: #05040f;
+ --bg-2: #120c25;
+ --bg-card: rgba(14, 12, 28, 0.76);
+ --bg-card-strong: rgba(28, 22, 60, 0.82);
+ --border-soft: rgba(255, 255, 255, 0.08);
+ --accent: #ff6a3d;
+ --accent-strong: #ffb347;
+ --accent-soft: rgba(255, 106, 61, 0.2);
+ --text-primary: #f8f9ff;
+ --text-muted: rgba(248, 249, 255, 0.7);
+ --success: #60f5d2;
+ --warning: #ffe15d;
+ --danger: #ff4f6d;
+ --shadow: 0 30px 60px -40px rgba(0, 0, 0, 0.85);
+ --glass-sheen: rgba(255, 255, 255, 0.08);
+ --hero-highlight: rgba(255, 122, 102, 0.35);
+ --grid-color: rgba(255, 255, 255, 0.06);
+ --bg-glow: rgba(87, 36, 180, 0.26);
+ --meta-card-bg: rgba(255, 255, 255, 0.06);
+ --meta-card-accent: rgba(255, 106, 61, 0.2);
+ --momentum-card-bg: rgba(5, 4, 20, 0.7);
+ --momentum-card-border: rgba(255, 255, 255, 0.08);
+ --momentum-card-overlay: rgba(255, 255, 255, 0.15);
+ --accent-ring: rgba(255, 165, 0, 0.4);
+ --table-row-alt: rgba(255, 255, 255, 0.04);
+ --table-row-hover: rgba(255, 255, 255, 0.08);
+ --table-row-self: rgba(96, 245, 210, 0.12);
+ --pace-track: rgba(255, 255, 255, 0.12);
+ --stealth-bg: rgba(255, 228, 82, 0.14);
+ --stealth-border: rgba(255, 228, 82, 0.5);
+ --ghost-border: rgba(255, 255, 255, 0.24);
+ --ghost-hover: rgba(255, 255, 255, 0.08);
+ --week-card-bg: rgba(5, 4, 20, 0.75);
+ --week-card-border: rgba(255, 255, 255, 0.08);
+ --input-surface: rgba(10, 10, 20, 0.82);
+ --input-surface-focus: rgba(18, 18, 34, 0.92);
+ --input-border: rgba(255, 255, 255, 0.16);
+ --input-border-strong: rgba(255, 255, 255, 0.14);
+ --input-bg-muted: rgba(255, 255, 255, 0.05);
+ --input-bg-focus: rgba(255, 255, 255, 0.07);
+ --day-chip-bg: rgba(255, 255, 255, 0.08);
+ --day-chip-today-bg: rgba(96, 245, 210, 0.16);
+ --code-bg: rgba(255, 255, 255, 0.08);
+ --chart-grid: rgba(248, 249, 255, 0.16);
+ --chart-tooltip-bg: rgba(5, 7, 22, 0.94);
+ --chart-tooltip-border: rgba(255, 255, 255, 0.12);
+ --cta-shadow: 0 15px 35px -25px rgba(255, 106, 61, 0.9);
+ --cta-shadow-hover: 0 24px 40px -30px rgba(255, 106, 61, 0.9);
+ color-scheme: dark;
+}
+
+body[data-theme="light"] {
+ --bg-1: #f5f7ff;
+ --bg-2: #dbeafe;
+ --bg-card: rgba(255, 255, 255, 0.9);
+ --bg-card-strong: rgba(255, 255, 255, 0.96);
+ --border-soft: rgba(15, 23, 42, 0.08);
+ --accent: #ea580c;
+ --accent-strong: #d97706;
+ --accent-soft: rgba(234, 88, 12, 0.16);
+ --text-primary: #0f172a;
+ --text-muted: rgba(15, 23, 42, 0.65);
+ --success: #0f766e;
+ --warning: #ca8a04;
+ --danger: #dc2626;
+ --shadow: 0 30px 60px -45px rgba(15, 23, 42, 0.35);
+ --glass-sheen: rgba(15, 23, 42, 0.08);
+ --hero-highlight: rgba(250, 204, 21, 0.28);
+ --grid-color: rgba(15, 23, 42, 0.12);
+ --bg-glow: rgba(59, 130, 246, 0.18);
+ --meta-card-bg: rgba(15, 23, 42, 0.06);
+ --meta-card-accent: rgba(234, 88, 12, 0.12);
+ --momentum-card-bg: rgba(255, 255, 255, 0.92);
+ --momentum-card-border: rgba(15, 23, 42, 0.08);
+ --momentum-card-overlay: rgba(148, 163, 184, 0.16);
+ --accent-ring: rgba(234, 179, 8, 0.45);
+ --table-row-alt: rgba(148, 163, 184, 0.16);
+ --table-row-hover: rgba(148, 163, 184, 0.24);
+ --table-row-self: rgba(14, 165, 233, 0.18);
+ --pace-track: rgba(148, 163, 184, 0.35);
+ --stealth-bg: rgba(234, 179, 8, 0.18);
+ --stealth-border: rgba(234, 179, 8, 0.45);
+ --ghost-border: rgba(15, 23, 42, 0.14);
+ --ghost-hover: rgba(15, 23, 42, 0.08);
+ --week-card-bg: rgba(255, 255, 255, 0.88);
+ --week-card-border: rgba(15, 23, 42, 0.08);
+ --input-surface: rgba(255, 255, 255, 0.94);
+ --input-surface-focus: rgba(255, 255, 255, 1);
+ --input-border: rgba(148, 163, 184, 0.45);
+ --input-border-strong: rgba(148, 163, 184, 0.45);
+ --input-bg-muted: rgba(148, 163, 184, 0.16);
+ --input-bg-focus: rgba(30, 64, 175, 0.12);
+ --day-chip-bg: rgba(148, 163, 184, 0.18);
+ --day-chip-today-bg: rgba(14, 165, 233, 0.26);
+ --code-bg: rgba(191, 219, 254, 0.6);
+ --chart-grid: rgba(148, 163, 184, 0.3);
+ --chart-tooltip-bg: rgba(255, 255, 255, 0.95);
+ --chart-tooltip-border: rgba(148, 163, 184, 0.3);
+ --cta-shadow: 0 18px 36px -26px rgba(234, 88, 12, 0.55);
+ --cta-shadow-hover: 0 26px 48px -28px rgba(234, 88, 12, 0.5);
+ color-scheme: light;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body.challenge-body {
+ margin: 0;
+ font-family: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ background: radial-gradient(circle at top, var(--bg-glow), transparent 55%),
+ linear-gradient(140deg, var(--bg-1) 0%, var(--bg-2) 70%);
+ color: var(--text-primary);
+ min-height: 100vh;
+ padding: 0 18px 80px;
+ position: relative;
+ transition: background 0.4s ease, color 0.3s ease;
+}
+
+.backdrop {
+ position: fixed;
+ inset: 0;
+ overflow: hidden;
+ z-index: -1;
+}
+
+.backdrop__grid {
+ position: absolute;
+ inset: 0;
+ background-image: radial-gradient(var(--grid-color) 1px, transparent 0);
+ background-size: 60px 60px;
+ opacity: 0.22;
+ transform: translateZ(0);
+}
+
+.backdrop__orb {
+ position: absolute;
+ filter: blur(140px);
+ width: 420px;
+ height: 420px;
+ border-radius: 50%;
+ opacity: 0.45;
+ animation: float 22s linear infinite;
+}
+
+.backdrop__orb--one {
+ background: #8132ff;
+ top: -120px;
+ left: -120px;
+}
+
+.backdrop__orb--two {
+ background: #ff7136;
+ bottom: -160px;
+ right: -120px;
+ animation-delay: 4s;
+}
+
+@keyframes float {
+ 0% {
+ transform: translate3d(0, 0, 0) scale(1);
+ }
+ 50% {
+ transform: translate3d(40px, -30px, 0) scale(1.06);
+ }
+ 100% {
+ transform: translate3d(0, 0, 0) scale(1);
+ }
+}
+
+.challenge-hero {
+ max-width: 1080px;
+ margin: 0 auto;
+ padding: 120px 0 32px;
+}
+
+.challenge-hero__inner {
+ background: linear-gradient(120deg, var(--glass-sheen), transparent);
+ border: 1px solid var(--border-soft);
+ border-radius: var(--radius);
+ padding: 40px;
+ box-shadow: var(--shadow);
+ position: relative;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+}
+
+.challenge-hero__inner::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(circle at top right, var(--hero-highlight), transparent 55%);
+ mix-blend-mode: screen;
+}
+
+.theme-switcher {
+ position: absolute;
+ top: 24px;
+ right: 24px;
+ display: inline-flex;
+ align-items: center;
+ gap: 12px;
+ z-index: 10;
+}
+
+.theme-switcher__label {
+ font-size: 0.75rem;
+ letter-spacing: 0.24em;
+ text-transform: uppercase;
+ color: var(--text-muted);
+ font-weight: 600;
+}
+
+.theme-switcher__button {
+ position: relative;
+ width: 60px;
+ height: 32px;
+ border-radius: 999px;
+ border: 1px solid var(--border-soft);
+ background: var(--bg-card-strong);
+ padding: 4px 8px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 6px;
+ color: var(--text-muted);
+ cursor: pointer;
+ transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
+}
+
+.theme-switcher__button:hover {
+ border-color: var(--accent);
+ box-shadow: 0 12px 28px -22px rgba(15, 23, 42, 0.6);
+}
+
+body[data-theme="dark"] .theme-switcher__button:hover {
+ box-shadow: 0 12px 28px -22px rgba(0, 0, 0, 0.7);
+}
+
+.theme-switcher__icon {
+ pointer-events: none;
+ font-size: 1.05rem;
+ line-height: 1;
+ opacity: 0.45;
+ transition: opacity 0.3s ease, transform 0.3s ease;
+}
+
+.theme-switcher__button[data-state="light"] .theme-switcher__icon--sun,
+.theme-switcher__button[data-state="dark"] .theme-switcher__icon--moon {
+ opacity: 0.95;
+ transform: translateY(-1px);
+}
+
+.theme-switcher__thumb {
+ position: absolute;
+ top: 4px;
+ left: 6px;
+ width: 22px;
+ height: 22px;
+ border-radius: 999px;
+ background: var(--text-primary);
+ box-shadow: 0 12px 24px -18px rgba(15, 23, 42, 0.9);
+ transition: transform 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
+}
+
+.theme-switcher__button[data-state="light"] .theme-switcher__thumb {
+ transform: translateX(26px);
+}
+
+body[data-theme="light"] .theme-switcher__thumb {
+ box-shadow: 0 10px 24px -18px rgba(15, 23, 42, 0.4);
+}
+
+.shadcn-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-soft);
+ border-radius: calc(var(--radius) - 8px);
+ box-shadow: var(--shadow);
+ backdrop-filter: blur(10px);
+ transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
+}
+
+.challenge-hero__tag {
+ font-family: "Chakra Petch", "Space Grotesk", sans-serif;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ font-size: 0.85rem;
+ color: var(--accent-strong);
+ margin: 0 0 12px;
+}
+
+.challenge-hero__title {
+ font-size: clamp(2.4rem, 5vw, 3.6rem);
+ margin: 0 0 14px;
+}
+
+.challenge-hero__subtitle {
+ font-size: 1.1rem;
+ line-height: 1.6;
+ color: var(--text-muted);
+ margin: 0;
+ max-width: 720px;
+}
+
+.challenge-hero__cta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px;
+ margin-top: 26px;
+}
+
+.cta-link {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 14px 22px;
+ border-radius: 999px;
+ background: var(--accent);
+ color: var(--text-primary);
+ text-decoration: none;
+ font-weight: 600;
+ box-shadow: var(--cta-shadow);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.cta-link:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--cta-shadow-hover);
+}
+
+.cta-link--ghost {
+ background: transparent;
+ border: 1px solid var(--ghost-border);
+ backdrop-filter: blur(6px);
+ box-shadow: none;
+}
+
+.challenge-main {
+ max-width: 1080px;
+ margin: 0 auto;
+ display: grid;
+ gap: 28px;
+}
+
+.panel {
+ background: var(--bg-card);
+ border: 1px solid var(--border-soft);
+ border-radius: var(--radius);
+ padding: 32px;
+ box-shadow: var(--shadow);
+ backdrop-filter: blur(10px);
+ transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
+}
+
+.panel--auth {
+ position: relative;
+ overflow: hidden;
+}
+
+.panel--dashboard {
+ display: grid;
+ gap: 26px;
+}
+
+.panel__header h2 {
+ margin: 0 0 6px;
+}
+
+.panel__header p {
+ margin: 0;
+ color: var(--text-muted);
+}
+
+.auth-form {
+ display: grid;
+ gap: 16px;
+ margin-top: 24px;
+}
+
+.auth-form__label {
+ font-weight: 600;
+ letter-spacing: 0.01em;
+}
+
+.auth-form__input {
+ width: 100%;
+ padding: 14px;
+ border-radius: 16px;
+ border: 1px solid var(--input-border);
+ background: var(--input-surface);
+ color: var(--text-primary);
+ font-size: 1rem;
+ transition: border-color 0.2s ease, background 0.2s ease;
+}
+
+.auth-form__input:focus {
+ outline: none;
+ border-color: var(--accent);
+ background: var(--input-surface-focus);
+}
+
+.auth-form__password {
+ position: relative;
+}
+
+.auth-form__toggle {
+ position: absolute;
+ top: 50%;
+ right: 12px;
+ transform: translateY(-50%);
+ background: transparent;
+ border: none;
+ font-size: 1.2rem;
+ cursor: pointer;
+}
+
+.auth-form__submit {
+ margin-top: 8px;
+ padding: 16px;
+ border-radius: 18px;
+ background: linear-gradient(120deg, var(--accent), #ff8a6a);
+ border: none;
+ color: var(--text-primary);
+ font-size: 1rem;
+ font-weight: 700;
+ cursor: pointer;
+ transition: transform 0.2s ease;
+}
+
+.auth-form__submit:hover {
+ transform: translateY(-2px);
+}
+
+.auth-form__error {
+ min-height: 18px;
+ margin: 0;
+ color: var(--danger);
+ font-weight: 600;
+}
+
+.dashboard-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 20px;
+}
+
+.dashboard-header__tag {
+ margin: 0;
+ text-transform: uppercase;
+ font-size: 0.75rem;
+ letter-spacing: 0.25em;
+ color: var(--accent-strong);
+}
+
+.dashboard-header__meta {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(120px, 1fr));
+ gap: 12px;
+}
+
+.meta-card {
+ padding: 14px 18px;
+ border-radius: 18px;
+ background: var(--meta-card-bg);
+ border: 1px solid var(--border-soft);
+ text-align: right;
+ transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
+}
+
+.meta-card--accent {
+ background: var(--meta-card-accent);
+}
+
+.meta-card__label {
+ font-size: 0.72rem;
+ text-transform: uppercase;
+ letter-spacing: 0.2em;
+ color: var(--text-muted);
+}
+
+.meta-card__value {
+ display: block;
+ font-size: 1.4rem;
+ font-weight: 700;
+}
+
+.panel-block {
+ background: var(--bg-card-strong);
+ border-radius: 26px;
+ border: 1px solid var(--border-soft);
+ padding: 28px;
+ transition: background 0.3s ease, border-color 0.3s ease;
+}
+
+.panel-block__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 16px;
+}
+
+.panel-block__header h3 {
+ margin: 0;
+}
+
+.panel-block__header p {
+ margin: 4px 0 0;
+ color: var(--text-muted);
+}
+
+.momentum-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 18px;
+ margin-top: 24px;
+}
+
+.momentum-card {
+ background: var(--momentum-card-bg);
+ border-radius: 20px;
+ border: 1px solid var(--momentum-card-border);
+ padding: 20px;
+ position: relative;
+ overflow: hidden;
+ transition: background 0.3s ease, border-color 0.3s ease;
+}
+
+.momentum-card::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(circle at top right, var(--momentum-card-overlay), transparent 55%);
+ mix-blend-mode: screen;
+}
+
+.momentum-card--highlight {
+ border-color: var(--accent-ring);
+}
+
+.momentum-card h4 {
+ margin: 0;
+ font-size: 0.96rem;
+ color: var(--text-muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+
+.momentum-card__value {
+ margin: 12px 0 6px;
+ font-size: 2rem;
+ font-weight: 700;
+}
+
+.momentum-card__note {
+ margin: 0;
+ color: var(--text-muted);
+ font-size: 0.9rem;
+}
+
+.panel-block--chart .panel-block__header {
+ align-items: flex-start;
+ flex-wrap: wrap;
+ gap: 20px;
+}
+
+.panel-block--chart .panel-block__header > div:first-child {
+ flex: 1 1 220px;
+}
+
+.chart-legend {
+ display: inline-flex;
+ align-items: center;
+ gap: 16px;
+ flex-wrap: wrap;
+ font-size: 0.85rem;
+ color: var(--text-muted);
+}
+
+.chart-legend__item {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.chart-legend__swatch {
+ width: 14px;
+ height: 14px;
+ border-radius: 999px;
+ display: inline-block;
+}
+
+.chart-legend__swatch--primary {
+ background: linear-gradient(135deg, var(--accent), var(--accent-strong));
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
+}
+
+.chart-legend__swatch--goal {
+ background: var(--success);
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
+
+body[data-theme="light"] .chart-legend__swatch--primary,
+body[data-theme="light"] .chart-legend__swatch--goal {
+ box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.12);
+}
+
+.chart-card {
+ position: relative;
+ overflow: hidden;
+ padding: 26px;
+ transition: background 0.3s ease, border-color 0.3s ease;
+}
+
+.chart-card::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(circle at 20% 15%, var(--accent-soft), transparent 60%);
+ pointer-events: none;
+}
+
+.chart-card__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 24px;
+}
+
+.chart-card__eyebrow {
+ margin: 0;
+ text-transform: uppercase;
+ letter-spacing: 0.28em;
+ font-size: 0.72rem;
+ color: var(--text-muted);
+}
+
+.chart-card__title {
+ margin: 6px 0 0;
+ font-size: 1.35rem;
+}
+
+.chart-card__stat {
+ padding: 14px 18px;
+ border-radius: 18px;
+ background: var(--bg-card-strong);
+ border: 1px solid var(--border-soft);
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ min-width: 120px;
+ gap: 4px;
+ transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
+}
+
+.chart-card__stat-label {
+ font-size: 0.7rem;
+ letter-spacing: 0.24em;
+ text-transform: uppercase;
+ color: var(--text-muted);
+ font-weight: 600;
+}
+
+.chart-card__stat-value {
+ font-size: 1.8rem;
+ font-weight: 700;
+ color: var(--accent-strong);
+ line-height: 1.1;
+}
+
+.chart-card__body {
+ margin-top: 24px;
+ height: 260px;
+}
+
+.chart-card__body canvas {
+ width: 100%;
+ height: 100%;
+}
+
+.leaderboard {
+ margin-top: 22px;
+ overflow-x: auto;
+}
+
+.leaderboard__table {
+ width: 100%;
+ border-collapse: collapse;
+ color: var(--text-primary);
+}
+
+.leaderboard__table th,
+.leaderboard__table td {
+ padding: 14px 16px;
+ border-bottom: 1px solid var(--border-soft);
+}
+
+.leaderboard__table tbody tr {
+ transition: background 0.2s ease;
+}
+
+.leaderboard__table tbody tr:nth-child(odd) {
+ background: var(--table-row-alt);
+}
+
+.leaderboard__table tbody tr:hover {
+ background: var(--table-row-hover);
+}
+
+.leaderboard__table .is-self {
+ background: var(--table-row-self);
+}
+
+.leaderboard__pace {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.leaderboard__pace-bar {
+ display: inline-block;
+ width: 70px;
+ height: 8px;
+ border-radius: 999px;
+ overflow: hidden;
+ background: var(--pace-track);
+}
+
+.leaderboard__pace-bar span {
+ display: block;
+ height: 100%;
+ border-radius: inherit;
+ background: linear-gradient(90deg, var(--success), #66b3ff);
+}
+
+.stealth-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 14px;
+ border-radius: 999px;
+ background: var(--stealth-bg);
+ border: 1px solid var(--stealth-border);
+ color: var(--warning);
+ font-weight: 600;
+ letter-spacing: 0.04em;
+}
+
+.panel-block__actions {
+ display: flex;
+ gap: 12px;
+}
+
+.ghost-button {
+ padding: 10px 18px;
+ border-radius: 999px;
+ border: 1px solid var(--ghost-border);
+ background: transparent;
+ color: var(--text-primary);
+ cursor: pointer;
+ font-size: 0.95rem;
+ transition: background 0.2s ease;
+}
+
+.ghost-button:hover {
+ background: var(--ghost-hover);
+}
+
+.ghost-button--wide {
+ width: 100%;
+ justify-content: center;
+ display: inline-flex;
+ align-items: center;
+}
+
+.ghost-button--upload {
+ text-align: center;
+}
+
+.weeks {
+ margin-top: 26px;
+ display: grid;
+ gap: 18px;
+}
+
+.week-card {
+ border-radius: 24px;
+ border: 1px solid var(--week-card-border);
+ background: var(--week-card-bg);
+ overflow: hidden;
+ transition: background 0.3s ease, border-color 0.3s ease;
+}
+
+.week-card__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 18px 22px;
+ cursor: pointer;
+}
+
+.week-card__header h4 {
+ margin: 0;
+ font-size: 1.1rem;
+}
+
+.week-card__header span {
+ color: var(--text-muted);
+ font-size: 0.9rem;
+}
+
+.week-card__body {
+ padding: 0 22px 22px;
+ display: none;
+}
+
+.week-card.is-open .week-card__body {
+ display: block;
+}
+
+.week-card__days {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.week-card__days th,
+.week-card__days td {
+ text-align: left;
+ padding: 12px 6px;
+ border-bottom: 1px solid var(--border-soft);
+}
+
+.week-card__days tbody tr:last-child td {
+ border-bottom: none;
+}
+
+.week-card__days input {
+ width: 120px;
+ padding: 10px 12px;
+ border-radius: 12px;
+ border: 1px solid var(--input-border-strong);
+ background: var(--input-bg-muted);
+ color: var(--text-primary);
+}
+
+.week-card__days input:focus {
+ outline: none;
+ border-color: var(--accent);
+ background: var(--input-bg-focus);
+}
+
+.week-card__total {
+ font-weight: 600;
+ color: var(--accent-strong);
+}
+
+.week-card__day-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.week-card__day-label span {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 26px;
+ height: 26px;
+ border-radius: 8px;
+ background: var(--day-chip-bg);
+ font-size: 0.8rem;
+}
+
+.week-card__day-label.is-today {
+ color: var(--success);
+}
+
+.week-card__day-label.is-today span {
+ background: var(--day-chip-today-bg);
+}
+
+.backup-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ gap: 14px;
+ margin-top: 18px;
+}
+
+.dashboard-footer {
+ text-align: center;
+ color: var(--text-muted);
+ font-size: 0.9rem;
+}
+
+code {
+ font-family: "Chakra Petch", "Space Grotesk", monospace;
+ background: var(--code-bg);
+ padding: 2px 6px;
+ border-radius: 6px;
+}
+
+@media (max-width: 760px) {
+ body.challenge-body {
+ padding: 0 12px 60px;
+ }
+
+ .challenge-hero {
+ padding-top: 90px;
+ }
+
+ .theme-switcher {
+ position: static;
+ align-self: flex-end;
+ margin-bottom: 20px;
+ order: -1;
+ }
+
+ .challenge-hero__inner {
+ padding: 32px 24px;
+ }
+
+ .panel {
+ padding: 26px;
+ }
+
+ .panel-block {
+ padding: 24px;
+ }
+
+ .dashboard-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .dashboard-header__meta {
+ width: 100%;
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .panel-block__header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .panel-block__actions {
+ width: 100%;
+ }
+
+ .panel-block__actions .ghost-button {
+ flex: 1;
+ text-align: center;
+ }
+
+ .panel-block--chart .panel-block__header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .chart-legend {
+ width: 100%;
+ justify-content: flex-start;
+ }
+
+ .chart-card {
+ padding: 22px;
+ }
+
+ .chart-card__header {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .chart-card__stat {
+ align-self: flex-start;
+ width: 100%;
+ flex-direction: row;
+ justify-content: space-between;
+ }
+
+ .chart-card__stat-value {
+ font-size: 1.6rem;
+ }
+
+ .chart-card__body {
+ height: 220px;
+ }
+}
Week total
+ ${formatNumber(weekTotal)} steps
+