diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml
new file mode 100644
index 0000000..a762a40
--- /dev/null
+++ b/.github/workflows/deploy-pages.yml
@@ -0,0 +1,41 @@
+name: Deploy GitHub Pages
+
+on:
+ push:
+ branches: [ "main" ]
+ paths:
+ - 'docs/**'
+ - '.github/workflows/deploy-pages.yml'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Check out source
+ uses: actions/checkout@v4
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v5
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: 'docs'
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
+
diff --git a/.gitignore b/.gitignore
index 0abd1dc..904970c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,3 @@
*.log
.md
!README.md
-docs/
\ No newline at end of file
diff --git a/docs/.nojekyll b/docs/.nojekyll
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/docs/.nojekyll
@@ -0,0 +1 @@
+
diff --git a/docs/app.js b/docs/app.js
new file mode 100644
index 0000000..1476828
--- /dev/null
+++ b/docs/app.js
@@ -0,0 +1,308 @@
+/**
+ * DailyCode Discord Bot - Landing Page & Permissions Logic
+ */
+
+const BOT_CLIENT_ID = "1559164347085885511";
+
+// Discord Permission Bit Flags (BigInt to handle 64-bit integer values safely)
+const PERMISSIONS = {
+ // General
+ VIEW_CHANNEL: 1024n, // 0x400
+ READ_MESSAGE_HISTORY: 65536n, // 0x10000
+ MANAGE_CHANNELS: 16n, // 0x10
+ MANAGE_ROLES: 268435456n, // 0x10000000
+ ADMINISTRATOR: 8n, // 0x8
+
+ // Text / Messaging
+ SEND_MESSAGES: 2048n, // 0x800
+ SEND_MESSAGES_IN_THREADS: 274877906944n, // 0x4000000000
+ EMBED_LINKS: 16384n, // 0x4000
+ ATTACH_FILES: 32768n, // 0x8000
+ ADD_REACTIONS: 64n, // 0x40
+ USE_EXTERNAL_EMOJIS: 262144n, // 0x40000
+ MENTION_EVERYONE: 131072n, // 0x20000
+};
+
+// Permission Presets
+const PRESETS = {
+ recommended: [
+ "VIEW_CHANNEL",
+ "READ_MESSAGE_HISTORY",
+ "SEND_MESSAGES",
+ "SEND_MESSAGES_IN_THREADS",
+ "EMBED_LINKS",
+ "ATTACH_FILES",
+ "ADD_REACTIONS",
+ "USE_EXTERNAL_EMOJIS",
+ "MENTION_EVERYONE"
+ ],
+ minimal: [
+ "VIEW_CHANNEL",
+ "READ_MESSAGE_HISTORY",
+ "SEND_MESSAGES",
+ "EMBED_LINKS"
+ ],
+ admin: [
+ "ADMINISTRATOR"
+ ]
+};
+
+document.addEventListener("DOMContentLoaded", () => {
+ initPermissionsCalculator();
+ initCommandFilters();
+ initCopyButtons();
+ initFaqAccordion();
+ initMobileMenu();
+ initMockupInteractivity();
+});
+
+/**
+ * Initializes the Discord permissions calculator
+ */
+function initPermissionsCalculator() {
+ const checkboxes = document.querySelectorAll(".perm-check");
+ const permValueEl = document.getElementById("perm-integer");
+ const inviteBtn = document.getElementById("custom-invite-btn");
+ const copyInviteBtn = document.getElementById("copy-invite-btn");
+ const presetBtns = document.querySelectorAll(".preset-btn");
+
+ if (!checkboxes.length || !permValueEl) return;
+
+ function calculatePermissions() {
+ let bitmask = 0n;
+ checkboxes.forEach((cb) => {
+ if (cb.checked && PERMISSIONS[cb.value]) {
+ bitmask |= PERMISSIONS[cb.value];
+ }
+ });
+
+ const bitmaskStr = bitmask.toString();
+ permValueEl.textContent = bitmaskStr;
+
+ const oauthUrl = `https://discord.com/oauth2/authorize?client_id=${BOT_CLIENT_ID}&permissions=${bitmaskStr}&scope=bot%20applications.commands`;
+
+ if (inviteBtn) {
+ inviteBtn.href = oauthUrl;
+ }
+
+ // Also update any primary CTA add-bot buttons that don't have hardcoded links
+ document.querySelectorAll(".dynamic-invite-link").forEach((link) => {
+ link.href = oauthUrl;
+ });
+
+ return oauthUrl;
+ }
+
+ // Checkbox change listener
+ checkboxes.forEach((cb) => {
+ cb.addEventListener("change", () => {
+ // Clear active preset highlight if modified manually
+ presetBtns.forEach((b) => b.classList.remove("active"));
+ calculatePermissions();
+ });
+ });
+
+ // Preset button clicks
+ presetBtns.forEach((btn) => {
+ btn.addEventListener("click", () => {
+ const presetName = btn.dataset.preset;
+ const targetPerms = PRESETS[presetName] || [];
+
+ presetBtns.forEach((b) => b.classList.remove("active"));
+ btn.classList.add("active");
+
+ checkboxes.forEach((cb) => {
+ cb.checked = targetPerms.includes(cb.value);
+ });
+
+ calculatePermissions();
+ });
+ });
+
+ // Copy customized invite URL
+ if (copyInviteBtn) {
+ copyInviteBtn.addEventListener("click", () => {
+ const url = calculatePermissions();
+ navigator.clipboard.writeText(url).then(() => {
+ showToast("Invite URL copied to clipboard!");
+ }).catch(() => {
+ // Fallback
+ showToast("Link ready: " + url.substring(0, 35) + "...");
+ });
+ });
+ }
+
+ // Initial calculation
+ calculatePermissions();
+}
+
+/**
+ * Slash commands category filtering
+ */
+function initCommandFilters() {
+ const filterBtns = document.querySelectorAll(".filter-btn");
+ const commandCards = document.querySelectorAll(".command-card");
+
+ filterBtns.forEach((btn) => {
+ btn.addEventListener("click", () => {
+ filterBtns.forEach((b) => b.classList.remove("active"));
+ btn.classList.add("active");
+
+ const category = btn.dataset.category;
+
+ commandCards.forEach((card) => {
+ if (category === "all" || card.dataset.category === category) {
+ card.style.display = "flex";
+ } else {
+ card.style.display = "none";
+ }
+ });
+ });
+ });
+}
+
+/**
+ * Click-to-copy handler (Brutalist)
+ */
+function initCopyButtons() {
+ document.querySelectorAll(".copy-btn").forEach((btn) => {
+ btn.addEventListener("click", (e) => {
+ e.stopPropagation();
+ const textToCopy = btn.dataset.copy;
+ if (!textToCopy) return;
+
+ navigator.clipboard.writeText(textToCopy).then(() => {
+ showToast(`COPIED: "${textToCopy}"`);
+ const originalContent = btn.innerHTML;
+ btn.innerHTML = `[✓ COPIED] `;
+ btn.style.background = "#FF8800";
+ btn.style.color = "#000000";
+ setTimeout(() => {
+ btn.innerHTML = originalContent;
+ btn.style.background = "";
+ btn.style.color = "";
+ }, 1500);
+ });
+ });
+ });
+}
+
+/**
+ * FAQ accordion toggle (Brutalist [+] / [-])
+ */
+function initFaqAccordion() {
+ const faqItems = document.querySelectorAll(".faq-item");
+
+ faqItems.forEach((item) => {
+ const questionBtn = item.querySelector(".faq-question");
+ const answer = item.querySelector(".faq-answer");
+ const icon = item.querySelector(".faq-toggle-icon");
+
+ if (!questionBtn || !answer) return;
+
+ questionBtn.addEventListener("click", () => {
+ const isOpen = item.classList.contains("active");
+
+ // Close all other items
+ faqItems.forEach((other) => {
+ if (other !== item) {
+ other.classList.remove("active");
+ const otherAns = other.querySelector(".faq-answer");
+ const otherIcon = other.querySelector(".faq-toggle-icon");
+ if (otherAns) otherAns.style.maxHeight = null;
+ if (otherIcon) otherIcon.textContent = "[+]";
+ }
+ });
+
+ // Toggle current
+ if (isOpen) {
+ item.classList.remove("active");
+ answer.style.maxHeight = null;
+ if (icon) icon.textContent = "[+]";
+ } else {
+ item.classList.add("active");
+ answer.style.maxHeight = answer.scrollHeight + 30 + "px";
+ if (icon) icon.textContent = "[−]";
+ }
+ });
+ });
+}
+
+/**
+ * Mobile navigation toggle
+ */
+function initMobileMenu() {
+ const toggle = document.querySelector(".mobile-toggle");
+ const menu = document.querySelector(".nav-menu");
+
+ if (!toggle || !menu) return;
+
+ toggle.addEventListener("click", () => {
+ menu.classList.toggle("open");
+ });
+
+ menu.querySelectorAll("a").forEach((link) => {
+ link.addEventListener("click", () => {
+ menu.classList.remove("open");
+ });
+ });
+}
+
+/**
+ * Mockup interactivity (fun responsive feedback for visitors)
+ */
+function initMockupInteractivity() {
+ const joinBtn = document.getElementById("mockup-join-btn");
+ const statusBtn = document.getElementById("mockup-status-btn");
+ const countSpan = document.getElementById("mockup-joined-count");
+
+ if (joinBtn && countSpan) {
+ let joined = false;
+ let baseCount = 14;
+
+ joinBtn.addEventListener("click", () => {
+ joined = !joined;
+ if (joined) {
+ countSpan.textContent = `${baseCount + 1} Coders`;
+ joinBtn.textContent = "✓ Joined!";
+ joinBtn.style.background = "#248046";
+ showToast("Simulated: You joined today's challenge!");
+ } else {
+ countSpan.textContent = `${baseCount} Coders`;
+ joinBtn.textContent = "Join Challenge";
+ joinBtn.style.background = "";
+ }
+ });
+ }
+
+ if (statusBtn) {
+ statusBtn.addEventListener("click", () => {
+ showToast("Triggered: /challenge status command preview");
+ });
+ }
+}
+
+/**
+ * Displays a brutalist toast notification
+ */
+function showToast(message) {
+ let toast = document.querySelector(".toast");
+ if (!toast) {
+ toast = document.createElement("div");
+ toast.className = "toast";
+ document.body.appendChild(toast);
+ }
+
+ toast.innerHTML = `
+ [OK]
+ ${message}
+ `;
+
+ toast.classList.add("show");
+
+ clearTimeout(window.toastTimer);
+ window.toastTimer = setTimeout(() => {
+ toast.classList.remove("show");
+ }, 2200);
+}
+
diff --git a/docs/assets/banner.png b/docs/assets/banner.png
new file mode 100644
index 0000000..0cdb0bd
Binary files /dev/null and b/docs/assets/banner.png differ
diff --git a/docs/assets/logo.png b/docs/assets/logo.png
new file mode 100644
index 0000000..08823ba
Binary files /dev/null and b/docs/assets/logo.png differ
diff --git a/docs/challenge-automation.md b/docs/challenge-automation.md
new file mode 100644
index 0000000..739fe6d
--- /dev/null
+++ b/docs/challenge-automation.md
@@ -0,0 +1,53 @@
+# Daily posting time
+
+Server managers can choose when the bot posts each day's problem:
+
+```text
+/challenge automate id:1 channel:#daily-challenges time:00:00
+```
+
+`00:00` is 12 AM (midnight). Use `12:00` for noon or `18:30` for 6:30 PM.
+The optional `time` uses 24-hour `HH:mm` format and the challenge's timezone,
+which is set when creating the challenge and defaults to `Africa/Casablanca`.
+Difficulty remains optional and defaults to Easy.
+
+Omitting `time` selects `00:00`. Existing automations also default to midnight
+after upgrading. Repeat `/challenge automate` with the channel and new time to
+change the schedule. Include the desired difficulty when reconfiguring.
+`/challenge status id:1` shows the saved posting time and timezone.
+
+The bot checks for due assignments about every minute by default. Posting happens
+on the first check at or after the selected time, so it is not guaranteed to occur
+at the exact second. If enabled or restarted after that time, it catches up with
+today's problem. It does not backfill older days or repost an announcement already
+attempted for today. Changing the time does not create a second problem that day.
+
+When a due assignment has no unused free problem at its selected difficulty,
+the bot browses LeetCode's catalog and imports a new problem automatically, then
+retries the assignment. No manual problem loading is required. Catalog requests
+run outside assignment transactions, and the retry rechecks the current schedule
+and automation settings before posting.
+
+Each fetch attempt scans at most three pages of 50 entries and makes at most three
+metadata requests. Scans continue on later checks when necessary. Upstream or
+import failures wait five minutes before retrying. If the catalog contains no new
+free problems at that difficulty, it checks again after six hours. Imported
+problems persist across restarts; the catalog cursor restarts from the beginning.
+These retries may delay a post, and the bot still catches up only for the current
+challenge day. It never changes the requested difficulty or repeats a problem
+within a challenge to fill a gap.
+
+The schedule follows local time when timezone offsets change. If daylight saving
+skips a selected time, that day's posting time shifts forward by the gap; if the
+time occurs twice, the first occurrence is used. Each day still has one assignment.
+
+The deadline remains the next midnight in the challenge timezone, including when
+posting later in the day. Submission checks still run about every five minutes.
+`/challenge stop-auto id:1` disables daily posting and automatic submission checks.
+
+Rebuild and restart the deployed bot to apply migration V10 and register the new
+Discord option:
+
+```bash
+docker compose up -d --build bot
+```
diff --git a/docs/challenge-status.md b/docs/challenge-status.md
new file mode 100644
index 0000000..974fcb9
--- /dev/null
+++ b/docs/challenge-status.md
@@ -0,0 +1,30 @@
+# Challenge status
+
+Use `/challenge status id:1` to view your saved progress in a challenge you have
+joined. The response is private and includes:
+
+- Total recorded completions.
+- Today's status in the challenge's timezone.
+- Whether automation is enabled, with its daily posting time and timezone.
+- Five assigned days per page, newest first, with problem titles and completion status.
+
+Use `/challenge status id:1 page:2` to see older assignments. Page defaults to 1.
+History remains accessible after the challenge ends or is cancelled.
+
+“No completion recorded” means the database has no verified completion; it does
+not prove that you missed the problem. Days whose deadline preceded your joining
+are labelled “Before you joined.” Dates without assignments are not history rows.
+
+This command does not fetch submissions or change progress. Use
+`/challenge check id:1` to check today's submissions, or include `date:YYYY-MM-DD`
+for an earlier assignment.
+
+After bringing this branch's changes onto OCI, rebuild and restart the bot:
+
+```bash
+docker compose up -d --build bot
+```
+
+The bot registers the subcommand when it connects to Discord. The daily posting
+time is stored by migration V10, which runs automatically at startup. See
+[daily posting time](challenge-automation.md) for scheduling options.
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..2d87b3f
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,807 @@
+
+
+
+
+
+ DailyCode — Brutalist LeetCode Discord Bot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // AUTOMATED DAILY CHALLENGES • 0 PASSWORDS (BIO VERIFICATION) • 5-MINUTE AUTO POLL • GUILD LEADERBOARDS • DEDUPLICATION ENGINE • ROLE PINGS • 100% FREE & OPEN SOURCE • // AUTOMATED DAILY CHALLENGES • 0 PASSWORDS (BIO VERIFICATION) • 5-MINUTE AUTO POLL • GUILD LEADERBOARDS • DEDUPLICATION ENGINE • ROLE PINGS • 100% FREE & OPEN SOURCE •
+
+
+
+
+
+
+
+
+
+
+ STATUS: ONLINE
+
+ LEETCODE API: SYNCED
+ SPRING BOOT 3
+
+
+
+
+
+
+ Daily LeetCode.
+ Directly in Discord.
+
+
+
+ Stop pasting problem links manually. DailyCode automates daily problem drops, verifies participant accounts using profile bio tokens without passwords, checks solutions every 5 minutes, and tracks server leaderboards.
+
+
+
+
+
+
+
+
+
+
# daily-coding-challenges
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DailyCode
+ BOT
+
+
+
+
@DailyCoders 🚀 Today's Drop is Live!
+
+
+
+ #15. 3Sum
+ Medium
+
+
+
+ Given an integer array nums, return all triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, j != k, and nums[i] + nums[j] + nums[k] == 0.
+
+
+
+
+
Active Coders
+
14 Joined
+
+
+
Sync Rate
+
Every 5 Minutes
+
+
+
Tags
+
Array, Two Pointers
+
+
+
Verification
+
Auto-Poll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
100%
+
Free & Open Source
+
+
+
+
5 MIN
+
Auto Solution Polling
+
+
+
JAVA 21
+
Spring Boot 3 + Postgres
+
+
+
+
+
+
+
+
// ARCHITECTURE
+
Engineered for Consistency
+
No fluff, no bloat. Pure functionality designed to keep developer communities active.
+
+
+
+
+
+
+
+ 01
+ CRON SCHEDULER
+
+
Automated Daily Drops
+
+ Schedule daily drops at any time using /challenge automate time:09:00 channel:#daily role:@Coders. The bot handles question selection and alerts automatically.
+
+
+
> Fully Hands-Off
+
+
+
+
+
+
+ 02
+ BIO VERIFY
+
+
Zero Password Risk
+
+ Connect your LeetCode handle with /leetcode connect, paste a short security token into your public bio summary, and verify in seconds with /leetcode verify.
+
+
+
> No Credentials Shared
+
+
+
+
+
+
+ 03
+ AUTO POLL
+
+
5-Minute Auto-Check
+
+ Participants don't need to manually post solutions. DailyCode runs a periodic background check that inspects recent submissions and marks completions automatically.
+
+
+
> Instant Verification
+
+
+
+
+
+
+ 04
+ STANDINGS
+
+
Server Leaderboard
+
+ Run /challenge leaderboard to see server rankings, streaks, and total solved counts. Friendly competition drives habitual problem solving.
+
+
+
> Live Guild Rankings
+
+
+
+
+
+
+ 05
+ DEDUPLICATION
+
+
No Repeat Problems
+
+ The bot tracks historical drops per guild and avoids repeated problems, ensuring members get diverse algorithm problems across arrays, trees, dynamic programming, and graphs.
+
+
+
> Unique Catalog Drops
+
+
+
+
+
+
+ 06
+ PINGS
+
+
Targeted Role Mentions
+
+ Avoid server-wide noise. Specify a role like @DailyCoders so only opted-in members receive notifications when the daily problem drops.
+
+
+
> Clean Server Comms
+
+
+
+
+
+
+
+
+
+
// SETUP GUIDE
+
Ready in 3 Commands
+
No external web dashboards. Everything operates through Discord native slash commands.
+
+
+
+
+
01
+
Invite Bot
+
Authorize DailyCode on your server with permissions to post embeds in your chosen channel.
+
> [ 1-Click OAuth2 Authorize ]
+
+
+
+
02
+
Set Automation
+
Configure the daily drop time, target channel, and optional role ping.
+
/challenge automate time:09:00 channel:#daily
+
+
+
+
03
+
Connect & Solve
+
Members connect their LeetCode account with a bio token and start solving daily.
+
/leetcode connect username:you
+
+
+
+
+
+
+
+
+
+
// CLI REFERENCE
+
Slash Commands
+
Click the copy button on any command to copy its syntax directly.
+
+
+
+ All Commands
+ Challenge & Drops
+ LeetCode Sync
+ Automation
+
+
+
+
+
+
+
+ /leetcode connect
+
+ [COPY]
+
+
+
Binds your Discord handle to your LeetCode username and provides a temporary verification token.
+
+
+ username: string
+
+
+
+
+
+
+
+ /leetcode verify
+
+ [COPY]
+
+
+
Verifies that the generated token is present in your public LeetCode profile bio and finalizes connection.
+
+
+ no arguments
+
+
+
+
+
+
+
+ /challenge automate
+
+ [COPY]
+
+
+
Schedules recurring daily challenge drops in a specific channel at a chosen hour (HH:mm) with optional role ping.
+
+
+ time: HH:mm
+ channel: #channel
+ role: @role (optional)
+
+
+
+
+
+
+
+ /challenge stop-auto
+
+ [COPY]
+
+
+
Deactivates automated daily challenge drops for the current server.
+
+
+ no arguments
+
+
+
+
+
+
+
+ /challenge create
+
+ [COPY]
+
+
+
Manually creates a new coding challenge for the server with a target number of problems.
+
+
+ name: string
+ target: integer
+
+
+
+
+
+
+
+ /challenge join
+
+ [COPY]
+
+
+
Enrolls your linked LeetCode handle into the active server challenge.
+
+
+ challenge_id: int (optional)
+
+
+
+
+
+
+
+ /challenge today
+
+ [COPY]
+
+
+
Displays today's active LeetCode question, difficulty, description snippet, tags, and problem link.
+
+
+ no arguments
+
+
+
+
+
+
+
+ /challenge status
+
+ [COPY]
+
+
+
Shows paginated participant progress, completion status, and active participants for the challenge.
+
+
+ challenge_id: int (optional)
+
+
+
+
+
+
+
+ /challenge check
+
+ [COPY]
+
+
+
Triggers an immediate verification check for participants without waiting for the 5-minute background cron.
+
+
+ no arguments
+
+
+
+
+
+
+
+ /challenge leaderboard
+
+ [COPY]
+
+
+
Displays the server leaderboard ranking users by completed challenges, streaks, and solve rate.
+
+
+ no arguments
+
+
+
+
+
+
+
+
+
+
+
+
// PERMISSION MATRIX
+
Discord Permission Calculator
+
Fine-tune the exact OAuth2 permissions bitmask to invite DailyCode with.
+
+
+
+
+ Presets:
+ [ Recommended ]
+ [ Minimal ]
+ [ Administrator ]
+
+
+
+
+
+
+
+
+
// TEXT & MESSAGING
+
+
+
+
+
+
+
+
+
+ // CALCULATED PERMISSION INTEGER
+ 274878486976
+
+
+
+
+
+
+
+
+
+
+
+
+
// INQUIRIES
+
Frequently Asked Questions
+
Common questions regarding security, polling, and hosting.
+
+
+
+
+
+ Is DailyCode free and open source?
+ [+]
+
+
+ Yes. DailyCode is 100% free under the MIT license. You can invite the hosted bot to your server or self-host your own container instance backed by PostgreSQL.
+
+
+
+
+
+ Do users ever need to provide their LeetCode password?
+ [+]
+
+
+ Never. DailyCode uses a secure bio token verification flow. The bot generates a temporary alphanumeric token. You paste it into your public LeetCode profile bio summary, run /leetcode verify, and remove it immediately after.
+
+
+
+
+
+ How does the bot verify accepted submissions?
+ [+]
+
+
+ An internal scheduled background task polls LeetCode's public recent accepted submissions every 5 minutes. If a participant solves the active question, the bot marks their challenge status as solved. You can also trigger an immediate check with /challenge check.
+
+
+
+
+
+ Can multiple Discord servers use the bot independently?
+ [+]
+
+
+ Yes. Every server maintains isolated challenge configurations, dedicated channels, deduplicated problem history, and distinct guild leaderboards.
+
+
+
+
+
+ How do I self-host DailyCode?
+ [+]
+
+
+ DailyCode is a Spring Boot 3 Java application. Clone the repository, provide your DISCORD_TOKEN and DATABASE_URL in environment variables, and run with Docker (docker compose up) or Maven (mvn spring-boot:run).
+
+
+
+
+
+
+
+
+
+
+
+
Level Up Your Server's Coding Habit
+
Add DailyCode to your server in 30 seconds and let automated problems fuel daily practice.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/styles.css b/docs/styles.css
new file mode 100644
index 0000000..30a8162
--- /dev/null
+++ b/docs/styles.css
@@ -0,0 +1,1199 @@
+/* ========================================================
+ DAILYCODE — BRUTALIST DARK DEVELOPER THEME
+ No blur. No fake glow. High contrast, sharp edges, hard shadows.
+ Color scheme strictly inspired by logo: #080808, #FF5E00, #FF8800, #FFA116, #FFFFFF
+ ======================================================== */
+
+:root {
+ --bg-page: #080809;
+ --bg-panel: #101013;
+ --bg-panel-hover: #16161a;
+ --bg-panel-contrast: #1a1a20;
+
+ --border-muted: #24242b;
+ --border-strong: #3e3e48;
+ --border-accent: #ff8800;
+ --border-white: #ffffff;
+
+ --accent-orange: #ff5e00;
+ --accent-amber: #ff8800;
+ --accent-gold: #ffa116;
+ --accent-yellow: #ffc01e;
+
+ --text-main: #f5f5f7;
+ --text-dim: #9b9ba8;
+ --text-muted: #626270;
+ --text-inverse: #080809;
+
+ --discord-blurple: #5865f2;
+
+ --font-display: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
+
+ --shadow-black-sm: 3px 3px 0px #000000;
+ --shadow-black: 5px 5px 0px #000000;
+ --shadow-accent-sm: 3px 3px 0px #ff8800;
+ --shadow-accent: 5px 5px 0px #ff8800;
+ --shadow-white-sm: 3px 3px 0px #ffffff;
+ --shadow-white: 5px 5px 0px #ffffff;
+}
+
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ scroll-behavior: smooth;
+ font-family: var(--font-display);
+ background-color: var(--bg-page);
+ color: var(--text-main);
+ line-height: 1.5;
+ -webkit-font-smoothing: antialiased;
+}
+
+body {
+ position: relative;
+ overflow-x: hidden;
+ min-height: 100vh;
+ background-color: var(--bg-page);
+}
+
+/* Subtle Stark Technical Grid - Behind content, above background */
+.grid-overlay {
+ position: fixed;
+ inset: 0;
+ background-image:
+ linear-gradient(to right, #1c1c24 1px, transparent 1px),
+ linear-gradient(to bottom, #1c1c24 1px, transparent 1px);
+ background-size: 40px 40px;
+ pointer-events: none;
+ z-index: 1;
+}
+
+.container {
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 24px;
+ position: relative;
+ z-index: 2;
+}
+
+/* Typography & Headings */
+h1, h2, h3, h4, h5, h6 {
+ font-family: var(--font-display);
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ text-transform: uppercase;
+ color: var(--text-main);
+}
+
+.mono-label {
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+.text-orange {
+ color: var(--accent-amber);
+}
+
+/* Navbar */
+.navbar {
+ position: sticky;
+ top: 0;
+ background: var(--bg-page);
+ border-bottom: 2px solid var(--border-strong);
+ height: 72px;
+ z-index: 100;
+}
+
+.nav-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 100%;
+}
+
+.brand-wrap {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+}
+
+.brand-logo-frame {
+ width: 40px;
+ height: 40px;
+ border: 2px solid var(--accent-amber);
+ background: #000;
+ box-shadow: 3px 3px 0px var(--accent-orange);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.brand-logo {
+ width: 32px;
+ height: 32px;
+ display: block;
+}
+
+.brand-title {
+ font-size: 1.35rem;
+ font-weight: 900;
+ letter-spacing: -0.02em;
+}
+
+.brand-badge {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ font-weight: 700;
+ padding: 3px 8px;
+ background: var(--accent-amber);
+ color: #000;
+ border: 1px solid #000;
+}
+
+.nav-menu {
+ display: flex;
+ align-items: center;
+ gap: 28px;
+ list-style: none;
+}
+
+.nav-menu a {
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: var(--text-dim);
+ text-transform: uppercase;
+ transition: color 0.15s ease;
+}
+
+.nav-menu a:hover {
+ color: var(--accent-amber);
+}
+
+.nav-actions {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.mobile-toggle {
+ display: none;
+ background: #000;
+ border: 2px solid var(--border-strong);
+ color: var(--text-main);
+ padding: 8px 12px;
+ font-family: var(--font-mono);
+ font-size: 1.1rem;
+ cursor: pointer;
+}
+
+/* Brutalist Buttons */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ font-family: var(--font-mono);
+ font-size: 0.88rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ padding: 10px 20px;
+ border-radius: 0px;
+ cursor: pointer;
+ text-decoration: none;
+ transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
+}
+
+.btn-primary {
+ background: var(--accent-amber);
+ color: #000000;
+ border: 2px solid #000000;
+ box-shadow: var(--shadow-white-sm);
+}
+
+.btn-primary:hover {
+ background: var(--accent-gold);
+ transform: translate(-2px, -2px);
+ box-shadow: 5px 5px 0px #ffffff;
+}
+
+.btn-primary:active {
+ transform: translate(2px, 2px);
+ box-shadow: 1px 1px 0px #ffffff;
+}
+
+.btn-secondary {
+ background: var(--bg-panel);
+ color: var(--text-main);
+ border: 2px solid var(--border-strong);
+ box-shadow: var(--shadow-black-sm);
+}
+
+.btn-secondary:hover {
+ background: var(--bg-panel-contrast);
+ border-color: var(--text-main);
+ transform: translate(-2px, -2px);
+ box-shadow: var(--shadow-white-sm);
+}
+
+.btn-secondary:active {
+ transform: translate(2px, 2px);
+ box-shadow: 1px 1px 0px var(--text-main);
+}
+
+.btn-accent {
+ background: #000000;
+ color: var(--accent-amber);
+ border: 2px solid var(--accent-amber);
+ box-shadow: var(--shadow-accent-sm);
+}
+
+.btn-accent:hover {
+ background: var(--accent-amber);
+ color: #000000;
+ transform: translate(-2px, -2px);
+ box-shadow: var(--shadow-accent);
+}
+
+.btn-accent:active {
+ transform: translate(2px, 2px);
+ box-shadow: 1px 1px 0px var(--accent-amber);
+}
+
+.btn-sm {
+ padding: 6px 14px;
+ font-size: 0.8rem;
+}
+
+.btn-lg {
+ padding: 14px 28px;
+ font-size: 1rem;
+ letter-spacing: 0.03em;
+}
+
+.btn-icon {
+ width: 16px;
+ height: 16px;
+ fill: currentColor;
+ flex-shrink: 0;
+}
+
+/* Ticker Bar */
+.ticker-bar {
+ position: relative;
+ z-index: 5;
+ background: var(--accent-amber);
+ color: #000000;
+ border-bottom: 2px solid #000000;
+ overflow: hidden;
+ white-space: nowrap;
+ padding: 8px 0;
+ font-family: var(--font-mono);
+ font-size: 0.82rem;
+ font-weight: 800;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+}
+
+.ticker-track {
+ display: inline-block;
+ animation: ticker-slide 28s linear infinite;
+}
+
+@keyframes ticker-slide {
+ from { transform: translateX(0); }
+ to { transform: translateX(-50%); }
+}
+
+/* Hero Section */
+.hero-section {
+ padding: 70px 0 60px;
+ border-bottom: 2px solid var(--border-muted);
+}
+
+.hero-status-strip {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ margin-bottom: 28px;
+ flex-wrap: wrap;
+}
+
+.status-tag {
+ font-family: var(--font-mono);
+ font-size: 0.76rem;
+ font-weight: 700;
+ padding: 4px 10px;
+ background: #111115;
+ border: 1px solid var(--border-strong);
+ color: var(--text-dim);
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ background: var(--accent-amber);
+}
+
+.hero-grid {
+ display: grid;
+ grid-template-columns: 1.15fr 0.85fr;
+ gap: 40px;
+ align-items: center;
+}
+
+.hero-headline {
+ font-size: clamp(2.4rem, 4.8vw, 4.4rem);
+ font-weight: 900;
+ line-height: 1.05;
+ margin-bottom: 24px;
+ letter-spacing: -0.04em;
+}
+
+.hero-desc {
+ font-size: 1.1rem;
+ color: var(--text-dim);
+ margin-bottom: 32px;
+ line-height: 1.6;
+ max-width: 580px;
+}
+
+.hero-cta-row {
+ display: flex;
+ gap: 14px;
+ flex-wrap: wrap;
+}
+
+/* Brutalist Discord / Terminal Mockup Card */
+.mockup-card {
+ background: var(--bg-panel);
+ border: 2px solid var(--border-strong);
+ box-shadow: 8px 8px 0px var(--accent-amber);
+ position: relative;
+}
+
+.mockup-bar {
+ background: #181820;
+ border-bottom: 2px solid var(--border-strong);
+ padding: 10px 16px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ font-weight: 700;
+ color: var(--text-dim);
+}
+
+.mockup-dots {
+ display: flex;
+ gap: 6px;
+}
+
+.mockup-dot {
+ width: 10px;
+ height: 10px;
+ background: var(--border-strong);
+}
+
+.mockup-inner {
+ padding: 22px;
+}
+
+.mockup-user-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 12px;
+}
+
+.mockup-avatar-frame {
+ width: 38px;
+ height: 38px;
+ border: 2px solid var(--accent-amber);
+ background: #000;
+ flex-shrink: 0;
+}
+
+.mockup-avatar-frame img {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.mockup-bot-name {
+ font-weight: 800;
+ font-size: 0.95rem;
+}
+
+.mockup-bot-pill {
+ font-family: var(--font-mono);
+ background: var(--discord-blurple);
+ color: #fff;
+ font-size: 0.65rem;
+ font-weight: 800;
+ padding: 2px 5px;
+}
+
+.mockup-role-tag {
+ font-family: var(--font-mono);
+ display: inline-block;
+ background: #1c1d2e;
+ color: #9aa5ff;
+ border: 1px solid #3c4485;
+ padding: 3px 8px;
+ font-size: 0.8rem;
+ font-weight: 700;
+ margin-bottom: 14px;
+}
+
+/* Problem Box Inside Mockup */
+.mockup-problem-box {
+ border: 2px solid var(--border-strong);
+ border-left: 6px solid var(--accent-amber);
+ background: #0b0b0e;
+ padding: 16px;
+}
+
+.mockup-problem-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 8px;
+ gap: 10px;
+}
+
+.mockup-problem-title {
+ font-size: 1.05rem;
+ font-weight: 800;
+ color: #ffffff;
+}
+
+.mockup-difficulty-badge {
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ background: var(--accent-amber);
+ color: #000;
+ padding: 2px 7px;
+ border: 1px solid #000;
+}
+
+.mockup-problem-desc {
+ font-size: 0.86rem;
+ color: var(--text-dim);
+ margin-bottom: 14px;
+ line-height: 1.5;
+}
+
+.mockup-matrix {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
+ border-top: 1px solid var(--border-muted);
+ border-bottom: 1px solid var(--border-muted);
+ padding: 10px 0;
+ margin-bottom: 14px;
+}
+
+.matrix-item {
+ font-family: var(--font-mono);
+ font-size: 0.76rem;
+}
+
+.matrix-label {
+ color: var(--text-muted);
+ text-transform: uppercase;
+ font-size: 0.68rem;
+ margin-bottom: 2px;
+}
+
+.matrix-val {
+ color: #ffffff;
+ font-weight: 700;
+}
+
+.mockup-btns-row {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.mockup-action-btn {
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ padding: 6px 12px;
+ background: var(--bg-panel-contrast);
+ color: var(--text-main);
+ border: 1px solid var(--border-strong);
+ cursor: pointer;
+ transition: all 0.1s ease;
+}
+
+.mockup-action-btn:hover {
+ background: var(--accent-amber);
+ color: #000;
+ border-color: #000;
+}
+
+.mockup-action-btn.active {
+ background: var(--accent-amber);
+ color: #000;
+}
+
+/* Stats Ribbon */
+.stats-strip {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ border-bottom: 2px solid var(--border-muted);
+ position: relative;
+ z-index: 2;
+}
+
+.stats-box {
+ padding: 28px 24px;
+ background: var(--bg-panel);
+ border-right: 2px solid var(--border-muted);
+ text-align: left;
+}
+
+.stats-box:last-child {
+ border-right: none;
+}
+
+.stats-num {
+ font-size: 2rem;
+ font-weight: 900;
+ color: var(--accent-amber);
+ margin-bottom: 4px;
+ line-height: 1;
+}
+
+.stats-desc {
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ text-transform: uppercase;
+ color: var(--text-dim);
+}
+
+/* Sections Common */
+.section {
+ padding: 80px 0;
+ border-bottom: 2px solid var(--border-muted);
+}
+
+.section-head {
+ margin-bottom: 44px;
+}
+
+.section-subtitle {
+ color: var(--text-dim);
+ font-size: 1.05rem;
+ margin-top: 8px;
+ max-width: 650px;
+}
+
+/* Features Stark Matrix */
+.features-matrix {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 20px;
+}
+
+.feature-box {
+ background: var(--bg-panel);
+ border: 2px solid var(--border-strong);
+ padding: 28px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ box-shadow: var(--shadow-black);
+ transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
+}
+
+.feature-box:hover {
+ border-color: var(--accent-amber);
+ box-shadow: 6px 6px 0px var(--accent-amber);
+ transform: translate(-2px, -2px);
+}
+
+.feature-box-top {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid var(--border-muted);
+}
+
+.feature-idx {
+ font-family: var(--font-mono);
+ font-size: 1.1rem;
+ font-weight: 900;
+ color: var(--accent-amber);
+}
+
+.feature-box-title {
+ font-size: 1.25rem;
+ margin-bottom: 10px;
+}
+
+.feature-box-desc {
+ font-size: 0.92rem;
+ color: var(--text-dim);
+ line-height: 1.6;
+ margin-bottom: 18px;
+}
+
+.feature-tag {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ padding: 3px 8px;
+ background: #000;
+ border: 1px solid var(--border-strong);
+ color: var(--accent-amber);
+ display: inline-block;
+ width: fit-content;
+}
+
+/* 3 Steps Pipeline */
+.steps-matrix {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 24px;
+}
+
+.step-box {
+ background: var(--bg-panel);
+ border: 2px solid var(--border-strong);
+ padding: 32px 24px;
+ box-shadow: var(--shadow-black);
+ position: relative;
+}
+
+.step-num-big {
+ font-size: 3rem;
+ font-weight: 900;
+ color: var(--border-strong);
+ line-height: 1;
+ margin-bottom: 12px;
+}
+
+.step-box:hover .step-num-big {
+ color: var(--accent-amber);
+}
+
+.step-box-title {
+ font-size: 1.2rem;
+ margin-bottom: 8px;
+}
+
+.step-box-desc {
+ color: var(--text-dim);
+ font-size: 0.92rem;
+ margin-bottom: 18px;
+}
+
+.step-code-bar {
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ background: #000;
+ border: 1px solid var(--border-strong);
+ padding: 8px 12px;
+ color: var(--accent-gold);
+}
+
+/* Slash Commands Explorer */
+.commands-controls {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 28px;
+ flex-wrap: wrap;
+}
+
+.filter-btn {
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ padding: 8px 14px;
+ background: var(--bg-panel);
+ color: var(--text-dim);
+ border: 2px solid var(--border-strong);
+ cursor: pointer;
+}
+
+.filter-btn:hover {
+ color: #fff;
+ border-color: #fff;
+}
+
+.filter-btn.active {
+ background: var(--accent-amber);
+ color: #000;
+ border-color: #000;
+ box-shadow: 3px 3px 0px #ffffff;
+}
+
+.commands-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 16px;
+}
+
+.command-card {
+ background: var(--bg-panel);
+ border: 2px solid var(--border-strong);
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ box-shadow: 4px 4px 0px #000;
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
+}
+
+.command-card:hover {
+ border-color: var(--accent-amber);
+ box-shadow: 4px 4px 0px var(--accent-amber);
+}
+
+.command-card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ gap: 12px;
+}
+
+.command-name {
+ font-family: var(--font-mono);
+ font-size: 0.95rem;
+ font-weight: 700;
+ color: var(--accent-gold);
+ background: #000;
+ border: 1px solid var(--border-strong);
+ padding: 4px 8px;
+}
+
+.copy-btn {
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ background: var(--bg-panel-contrast);
+ color: var(--text-main);
+ border: 1px solid var(--border-strong);
+ padding: 4px 10px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.copy-btn:hover {
+ background: var(--accent-amber);
+ color: #000;
+ border-color: #000;
+}
+
+.command-card-desc {
+ font-size: 0.9rem;
+ color: var(--text-dim);
+ margin-bottom: 14px;
+}
+
+.command-args {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+}
+
+.arg-badge {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ background: #000;
+ border: 1px solid var(--border-muted);
+ color: var(--text-muted);
+ padding: 2px 6px;
+}
+
+.arg-badge.req {
+ color: var(--accent-amber);
+ border-color: var(--accent-orange);
+}
+
+/* Permissions Matrix */
+.perms-panel {
+ background: var(--bg-panel);
+ border: 2px solid var(--border-strong);
+ box-shadow: 8px 8px 0px #000;
+ padding: 32px;
+}
+
+.perms-presets-bar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 28px;
+ padding-bottom: 16px;
+ border-bottom: 2px solid var(--border-muted);
+ flex-wrap: wrap;
+}
+
+.preset-btn {
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ background: #000;
+ color: var(--text-dim);
+ border: 2px solid var(--border-strong);
+ padding: 6px 14px;
+ cursor: pointer;
+}
+
+.preset-btn:hover {
+ color: #fff;
+ border-color: #fff;
+}
+
+.preset-btn.active {
+ background: var(--accent-amber);
+ color: #000;
+ border-color: #000;
+ box-shadow: 3px 3px 0px #ffffff;
+}
+
+.perms-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 32px;
+ margin-bottom: 36px;
+}
+
+.perm-col-title {
+ font-size: 1rem;
+ font-weight: 800;
+ color: var(--accent-amber);
+ margin-bottom: 14px;
+ padding-bottom: 6px;
+ border-bottom: 2px solid var(--border-strong);
+}
+
+.perm-items-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.perm-item-row {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ cursor: pointer;
+}
+
+.perm-item-row input[type="checkbox"] {
+ width: 18px;
+ height: 18px;
+ accent-color: var(--accent-amber);
+ margin-top: 3px;
+ cursor: pointer;
+ flex-shrink: 0;
+ border-radius: 0;
+}
+
+.perm-text-wrap {
+ display: flex;
+ flex-direction: column;
+}
+
+.perm-item-name {
+ font-family: var(--font-mono);
+ font-weight: 700;
+ font-size: 0.85rem;
+ color: #ffffff;
+}
+
+.perm-item-why {
+ font-size: 0.76rem;
+ color: var(--text-muted);
+}
+
+.perms-readout {
+ background: #000;
+ border: 2px solid var(--accent-amber);
+ box-shadow: 4px 4px 0px var(--accent-amber);
+ padding: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 20px;
+ flex-wrap: wrap;
+}
+
+.perms-calc-box {
+ display: flex;
+ flex-direction: column;
+}
+
+.perms-calc-label {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ color: var(--text-muted);
+}
+
+.perms-calc-val {
+ font-family: var(--font-mono);
+ font-size: 1.6rem;
+ font-weight: 900;
+ color: var(--accent-amber);
+}
+
+.perms-cta-wrap {
+ display: flex;
+ gap: 12px;
+ flex-wrap: wrap;
+}
+
+/* FAQ Accordion */
+.faq-stack {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ max-width: 900px;
+}
+
+.faq-item {
+ background: var(--bg-panel);
+ border: 2px solid var(--border-strong);
+ box-shadow: 4px 4px 0px #000;
+}
+
+.faq-question {
+ width: 100%;
+ padding: 18px 22px;
+ background: transparent;
+ border: none;
+ color: #ffffff;
+ font-family: var(--font-display);
+ font-size: 1.1rem;
+ font-weight: 800;
+ text-align: left;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ cursor: pointer;
+}
+
+.faq-toggle-icon {
+ font-family: var(--font-mono);
+ font-weight: 800;
+ font-size: 1.2rem;
+ color: var(--accent-amber);
+}
+
+.faq-answer {
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.25s ease-out, padding 0.25s ease-out;
+ padding: 0 22px;
+ color: var(--text-dim);
+ font-size: 0.94rem;
+ line-height: 1.65;
+}
+
+.faq-item.active .faq-answer {
+ padding-bottom: 20px;
+ border-top: 1px solid var(--border-muted);
+}
+
+/* Brutalist Banner / Callout */
+.action-callout {
+ background: #000;
+ border: 3px solid var(--accent-amber);
+ box-shadow: 8px 8px 0px var(--accent-amber);
+ padding: 50px 36px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 30px;
+ flex-wrap: wrap;
+}
+
+.callout-title {
+ font-size: clamp(1.8rem, 3.2vw, 2.6rem);
+ font-weight: 900;
+ margin-bottom: 8px;
+}
+
+.callout-desc {
+ color: var(--text-dim);
+ font-size: 1.05rem;
+ max-width: 600px;
+}
+
+/* Footer */
+.footer {
+ background: #040405;
+ border-top: 2px solid var(--border-strong);
+ padding: 48px 0 28px;
+ position: relative;
+ z-index: 2;
+}
+
+.footer-matrix {
+ display: grid;
+ grid-template-columns: 1.5fr 1fr 1fr 1fr;
+ gap: 32px;
+ margin-bottom: 40px;
+}
+
+.footer-col-title {
+ font-family: var(--font-mono);
+ font-size: 0.82rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ color: #ffffff;
+ margin-bottom: 14px;
+}
+
+.footer-col-links {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.footer-col-links a {
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ color: var(--text-dim);
+ transition: color 0.15s ease;
+}
+
+.footer-col-links a:hover {
+ color: var(--accent-amber);
+}
+
+.footer-bottom {
+ border-top: 1px solid var(--border-muted);
+ padding-top: 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ color: var(--text-muted);
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+/* Brutalist Toast */
+.toast {
+ position: fixed;
+ bottom: 24px;
+ right: 24px;
+ background: #000000;
+ color: #ffffff;
+ border: 2px solid var(--accent-amber);
+ box-shadow: 4px 4px 0px #000;
+ padding: 12px 18px;
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ z-index: 9999;
+ transform: translateY(120%);
+ opacity: 0;
+ transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
+ pointer-events: none;
+}
+
+.toast.show {
+ transform: translateY(0);
+ opacity: 1;
+}
+
+/* Responsive */
+@media (max-width: 992px) {
+ .hero-grid {
+ grid-template-columns: 1fr;
+ }
+ .features-matrix {
+ grid-template-columns: 1fr 1fr;
+ }
+ .steps-matrix {
+ grid-template-columns: 1fr;
+ }
+ .commands-grid {
+ grid-template-columns: 1fr;
+ }
+ .perms-grid {
+ grid-template-columns: 1fr;
+ }
+ .footer-matrix {
+ grid-template-columns: 1fr 1fr;
+ }
+ .stats-strip {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+@media (max-width: 640px) {
+ .features-matrix {
+ grid-template-columns: 1fr;
+ }
+ .footer-matrix {
+ grid-template-columns: 1fr;
+ }
+ .stats-strip {
+ grid-template-columns: 1fr;
+ }
+ .mobile-toggle {
+ display: block;
+ }
+ .nav-menu {
+ display: none;
+ position: absolute;
+ top: 72px;
+ left: 0;
+ right: 0;
+ background: var(--bg-page);
+ border-bottom: 2px solid var(--border-strong);
+ flex-direction: column;
+ padding: 20px;
+ gap: 16px;
+ }
+ .nav-menu.open {
+ display: flex;
+ }
+ .action-callout {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+}