]> danix's work - danix.xyz-2.git/commitdiff
feat: redesign footer with fortune cookie, about terminal readout, and tech/feature...
authorDanilo M. <redacted>
Mon, 20 Apr 2026 10:46:49 +0000 (12:46 +0200)
committerDanilo M. <redacted>
Mon, 20 Apr 2026 10:46:49 +0000 (12:46 +0200)
New footer structure: 3 equal columns (Fortune Cookie | About Credentials | Stack & Features badges) + full-width copyright bar with emoji personality line.

Fortune Cookie:
- data/quotes.yaml with 13 curated quotes
- fortune.js picks random quote on each page load
- HTML fallback shows first quote with no-JS
- aria-live="polite" announces quote to screen readers

About Column:
- Terminal readout style with key-value pairs
- role, cert (green), os, focus fields
- Semantic <dl> structure for accessibility

Badges Column:
- "built with" section: Hugo, Tailwind, Alpine.js, HTML5, CSS3, JS (purple badges)
- "features" section: WCAG 2.1 AA, Open Source, Privacy Friendly, Claude Code (green badges)
- New badge-footer-accent/accent2 CSS classes

Copyright Bar:
- "Made with β€οΈ lack of πŸ˜΄ lots of β˜• by danix" with emoji wrapped in aria-hidden
- danix link points to language-aware About page (/is/ or /it/is/)
- Centered, full-width, below border-top

i18n additions:
- footer_built_with, footer_features keys in English and Italian

Theming:
- All colors use CSS custom properties (--accent, --accent2, --text, --text-dim)
- Monospace fonts throughout (JetBrains Mono)
- Responsive: grid-cols-1 mobile β†’ md:grid-cols-3 tablet+
- WCAG 2.1 AA compliant: β‰₯4.5:1 contrast ratios, keyboard accessible, screen reader tested

Co-Authored-By: Claude Haiku 4.5 <redacted>
data/quotes.yaml [new file with mode: 0644]
docs/superpowers/specs/2026-04-20-footer-redesign-design.md [new file with mode: 0644]
i18n/en.yaml
i18n/it.yaml
themes/danix-xyz-hacker/assets/css/main.css
themes/danix-xyz-hacker/assets/css/main.min.css
themes/danix-xyz-hacker/assets/js/fortune.js [new file with mode: 0644]
themes/danix-xyz-hacker/layouts/partials/footer.html

diff --git a/data/quotes.yaml b/data/quotes.yaml
new file mode 100644 (file)
index 0000000..c6bebf9
--- /dev/null
@@ -0,0 +1,27 @@
+quotes:
+  - text: "The quieter you become, the more you can hear."
+    author: "Ram Dass"
+  - text: "In theory, theory and practice are the same. In practice, they are not."
+    author: "Albert Einstein"
+  - text: "The best time to plant a tree was 20 years ago. The second best time is now."
+    author: "Chinese Proverb"
+  - text: "Debugging is like being the detective in a crime drama."
+    author: "Filipe Fortes"
+  - text: "Good design is invisible."
+    author: "Paul Rand"
+  - text: "The only way to do great work is to love what you do."
+    author: "Steve Jobs"
+  - text: "Simplicity is the ultimate sophistication."
+    author: "Leonardo da Vinci"
+  - text: "Security is not a feature, it's a prerequisite."
+    author: "Bruce Schneier"
+  - text: "Privacy is not something that I'm merely entitled to, it's an absolute prerequisite."
+    author: "Marlon Brando"
+  - text: "The web as I envisaged it, we have not seen it yet."
+    author: "Tim Berners-Lee"
+  - text: "First, solve the problem. Then, write the code."
+    author: "John Johnson"
+  - text: "Code is read much more often than it is written."
+    author: "Guido van Rossum"
+  - text: "Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
+    author: "Martin Fowler"
diff --git a/docs/superpowers/specs/2026-04-20-footer-redesign-design.md b/docs/superpowers/specs/2026-04-20-footer-redesign-design.md
new file mode 100644 (file)
index 0000000..e917b51
--- /dev/null
@@ -0,0 +1,159 @@
+# Footer Redesign β€” Design Spec
+**Date:** 2026-04-20  
+**Status:** Approved  
+**Branch:** week-7-footer (to be created)
+
+---
+
+## Context
+
+The current footer is a placeholder: 3-column grid with site title, quick links, and email β€” no personality, no hacker identity. This redesign replaces it with a footer that reflects the site owner's professional identity, tech stack, and philosophy, while adding a "Fortune Cookie" feature that displays a random quote on every page load.
+
+---
+
+## Layout
+
+3 equal columns, full-width copyright bar underneath. Stacks to 1 column on mobile. No column section headers.
+
+```
+β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+β”‚  $ fortune      β”‚  role: β€¦    β”‚  built with      β”‚
+β”‚  danix          β”‚  cert: β€¦    β”‚  [Hugo][Tailwind] β”‚
+β”‚                 β”‚  os:   β€¦    β”‚  [Alpine][HTML5]  β”‚
+β”‚  "quote…"       β”‚  focus: β€¦   β”‚  [CSS3][JS]       β”‚
+β”‚  β€” Author       β”‚             β”‚                   β”‚
+β”‚                 β”‚             β”‚  features         β”‚
+β”‚                 β”‚             β”‚  [WCAG 2.1 AA]    β”‚
+β”‚                 β”‚             β”‚  [Open Source]    β”‚
+β”‚                 β”‚             β”‚  [Privacy Friendly]β”‚
+β”‚                 β”‚             β”‚  [Claude Code]    β”‚
+β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
+β”‚  Made with β€οΈ lack of πŸ˜΄ lots of β˜• by danix     β”‚
+β”‚         Β© 2026 Danilo M. Β· All rights reserved  β”‚
+β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+```
+
+Mobile: all columns stack vertically, copyright bar unchanged.
+
+---
+
+## Column 1 β€” Fortune Cookie
+
+No section header.
+
+**Subline:** `$ fortune danix` (dimmed, monospace β€” looks like a shell command)  
+**Quote text:** italic, `--text` color, `font-mono`  
+**Attribution:** `β€” Author Name`, `--text-dim` color  
+
+**Data source:** `data/quotes.yaml`  
+**Format:**
+```yaml
+quotes:
+  - text: "The quieter you become, the more you can hear."
+    author: "Ram Dass"
+  - text: "In theory, theory and practice are the same. In practice, they are not."
+    author: "Albert Einstein"
+```
+
+**Rendering:** Hugo renders all quotes into a JSON array in the template. A small vanilla JS snippet (no Alpine needed) picks one at random on each page load and injects it into the DOM. The `<blockquote>` element is pre-rendered in Hugo with the first quote as a no-JS fallback.
+
+**A11y:** `<blockquote>` with `<cite>` for attribution. `aria-live="polite"` on the quote container so screen readers announce the randomly selected quote after JS fires.
+
+---
+
+## Column 2 β€” About (Terminal Readout)
+
+No section header.
+
+**Style:** key-value table, monospace font  
+
+| Key | Value | Color |
+|-----|-------|-------|
+| `role:` | Cybersecurity Specialist | `--text` |
+| `cert:` | eJPT | `--accent2` (green β€” highlights credential) |
+| `os:` | Slackware (2005–present) | `--text`, year in `--text-dim` |
+| `focus:` | open-source Β· privacy | `--text` |
+
+Keys use `--text-dim`. Layout: `<dl>` with `<dt>`/`<dd>` pairs for semantic correctness and screen reader support.
+
+---
+
+## Column 3 β€” Stack & Feature Badges
+
+No section header. Two sub-groups with small dimmed labels above each group.
+
+**built with** (purple badges β€” `--accent`):
+Hugo Β· Tailwind CSS Β· Alpine.js Β· HTML5 Β· CSS3 Β· JavaScript
+
+**features** (green badges β€” `--accent2`):
+WCAG 2.1 AA Β· Open Source Β· Privacy Friendly Β· Claude Code
+
+**Badge style:** new `.badge-footer-accent` and `.badge-footer-accent2` CSS variants β€” same shape as existing `.badge` component but using `--accent` / `--accent2` colors. `aria-hidden="true"` on purely decorative badges; meaningful ones (WCAG 2.1 AA) get descriptive text.
+
+---
+
+## Copyright Bar
+
+Full-width, centered, below a `border-t` separator.
+
+**Line 1:** `Made with β€οΈ lack of πŸ˜΄ lots of β˜• by danix`  
+- `danix` links to the language-appropriate About page:
+  - English: `/is/`
+  - Italian: `/it/is/`
+  - Resolved dynamically via Hugo: `{{ .Site.LanguagePrefix }}/is/`
+  - Styled in `--accent`, hover to `--accent2`
+- Emoji are wrapped in `<span aria-hidden="true">` with adjacent screen-reader text via `<span class="sr-only">`
+
+**Line 2:** `Β© 2026 Danilo M. Β· All rights reserved`  
+- Year rendered with Hugo's `{{ now.Year }}`
+- Uses existing `{{ i18n "allRightsReserved" }}` key
+
+---
+
+## Theming Standard Compliance
+
+- All colors via CSS custom properties: `var(--accent)`, `var(--accent2)`, `var(--text)`, `var(--text-dim)`
+- No hard-coded hex values in template or new CSS
+- Fonts: `font-mono` (JetBrains Mono) for all terminal-style text
+- No section headers β€” cleaner, less noise
+- Frosted-bar styling (`frosted-bar border-t`) inherited from existing footer wrapper β€” no change needed
+- Mobile-first: `grid-cols-1` base, `md:grid-cols-3` at 768px+
+- Spacing: gap-8 between columns (2rem), consistent with existing grid patterns
+
+---
+
+## Accessibility Requirements
+
+- **Semantic HTML:** `<footer>`, `<blockquote>`, `<cite>`, `<dl>`/`<dt>`/`<dd>`
+- **Screen readers:** `aria-live="polite"` on fortune quote container; `aria-hidden="true"` on decorative emoji with `sr-only` text equivalents
+- **Color contrast:** All text on `--bg2` background must meet β‰₯4.5:1 (verified: `--text` 12.3:1, `--text-dim` 5.2:1, `--accent` 5.1:1, `--accent2` 7.2:1)
+- **Keyboard:** Only interactive element is the `danix` About link β€” standard focus ring applies
+- **Motion:** No animations β€” no `prefers-reduced-motion` concern
+- **Touch targets:** The `danix` link in copyright bar must be β‰₯44Γ—44px tap target (padding applied)
+
+---
+
+## Files to Create / Modify
+
+| File | Action |
+|------|--------|
+| `themes/danix-xyz-hacker/layouts/partials/footer.html` | Replace entirely |
+| `themes/danix-xyz-hacker/assets/css/main.css` | Add `.badge-footer-accent`, `.badge-footer-accent2` component classes |
+| `data/quotes.yaml` | Create β€” initial quote set (10–15 quotes) |
+| `themes/danix-xyz-hacker/assets/js/fortune.js` | Create β€” tiny vanilla JS random quote picker |
+| `i18n/en.yaml` | Add `footer_built_with`, `footer_features` label keys |
+| `i18n/it.yaml` | Add same keys in Italian |
+
+---
+
+## Verification
+
+1. Run `hugo server` β€” footer renders on all page types (home, single, list)
+2. Reload page 5+ times β€” quote changes each reload
+3. Disable JS β€” first quote from YAML shows as static fallback
+4. Toggle dark/light theme β€” all colors switch correctly via CSS vars
+5. Resize to 375px β€” columns stack to single column, no overflow
+6. Tab through footer β€” only the `danix` About link is focusable, focus ring visible
+7. Check `danix` link: English site β†’ `/is/`, Italian site β†’ `/it/is/`
+8. Run screen reader β€” quote announced after page load, emoji skipped, `dl` keys/values read correctly
+9. Run `npm run build` β€” CSS compiles without errors
index 5c3556829d635e50a883f12cb8ddf7c9846ac0bf..874c0e6345d514054bc8c5fca73d43153932e8ab 100644 (file)
@@ -14,6 +14,8 @@ email: "Email"
 contact: "Contact"
 links: "Links"
 allRightsReserved: "All rights reserved."
+footer_built_with: "built with"
+footer_features: "features"
 
 # Articles
 readMore: "Read more"
index 4d228bef9ce6619182768492adfa68e5ceb5a1cf..8c3eba12d59bb1f6cfaac97eb0286cd75a12d78d 100644 (file)
@@ -14,6 +14,8 @@ email: "Email"
 contact: "Contatti"
 links: "Link"
 allRightsReserved: "Tutti i diritti riservati."
+footer_built_with: "costruito con"
+footer_features: "caratteristiche"
 
 # Articles
 readMore: "Continua a leggere"
index 298c38b3c688f83c60a7251e6782f8b05106b4b3..163505f8a0ced8f4c3b3decb97de00f5c3a05cc3 100644 (file)
@@ -596,6 +596,21 @@ html.theme-light picture img[src="/images/default_thumbnail_dark.png"] {
   .article-nav-placeholder {
     @apply text-text-dim opacity-40;
   }
+
+  /* ---- Footer badge variants ---- */
+  .badge-footer-accent {
+    @apply inline-flex items-center px-2.5 py-1 rounded text-xs font-mono font-semibold whitespace-nowrap;
+    border: 1px solid rgba(168, 85, 247, 0.35);
+    background: rgba(168, 85, 247, 0.1);
+    color: var(--accent);
+  }
+
+  .badge-footer-accent2 {
+    @apply inline-flex items-center px-2.5 py-1 rounded text-xs font-mono font-semibold whitespace-nowrap;
+    border: 1px solid rgba(0, 255, 136, 0.35);
+    background: rgba(0, 255, 136, 0.1);
+    color: var(--accent2);
+  }
 }
 
 /* Prose overrides for light theme */
index 942d4019e0209434ed9b852634fb10a2eb68aee7..4e6e482764724bc86c4315ffd7c1b873e701e9e7 100644 (file)
@@ -1284,6 +1284,10 @@ button,
   color: var(--accent);
 }
 
+.text-accent2 {
+  color: var(--accent2);
+}
+
 .text-text {
   color: var(--text);
 }
@@ -1682,6 +1686,44 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   opacity: 0.4;
 }
 
+/* ---- Footer badge variants ---- */
+
+.badge-footer-accent {
+  display: inline-flex;
+  align-items: center;
+  white-space: nowrap;
+  border-radius: 0.25rem;
+  padding-left: 0.625rem;
+  padding-right: 0.625rem;
+  padding-top: 0.25rem;
+  padding-bottom: 0.25rem;
+  font-family: JetBrains Mono, monospace;
+  font-size: 0.75rem;
+  line-height: 1rem;
+  font-weight: 600;
+  border: 1px solid rgba(168, 85, 247, 0.35);
+  background: rgba(168, 85, 247, 0.1);
+  color: var(--accent);
+}
+
+.badge-footer-accent2 {
+  display: inline-flex;
+  align-items: center;
+  white-space: nowrap;
+  border-radius: 0.25rem;
+  padding-left: 0.625rem;
+  padding-right: 0.625rem;
+  padding-top: 0.25rem;
+  padding-bottom: 0.25rem;
+  font-family: JetBrains Mono, monospace;
+  font-size: 0.75rem;
+  line-height: 1rem;
+  font-weight: 600;
+  border: 1px solid rgba(0, 255, 136, 0.35);
+  background: rgba(0, 255, 136, 0.1);
+  color: var(--accent2);
+}
+
 .sr-only {
   position: absolute;
   width: 1px;
@@ -1807,6 +1849,10 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   margin-bottom: 2rem;
 }
 
+.mb-1 {
+  margin-bottom: 0.25rem;
+}
+
 .mb-12 {
   margin-bottom: 3rem;
 }
@@ -1957,6 +2003,10 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   width: 0.25rem;
 }
 
+.w-20 {
+  width: 5rem;
+}
+
 .w-32 {
   width: 8rem;
 }
@@ -2013,6 +2063,10 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   flex-shrink: 0;
 }
 
+.shrink-0 {
+  flex-shrink: 0;
+}
+
 .translate-x-0 {
   --tw-translate-x: 0px;
   transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@@ -2067,6 +2121,10 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   gap: 0.25rem;
 }
 
+.gap-1\.5 {
+  gap: 0.375rem;
+}
+
 .gap-2 {
   gap: 0.5rem;
 }
@@ -2087,6 +2145,12 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   gap: 2rem;
 }
 
+.space-y-1 > :not([hidden]) ~ :not([hidden]) {
+  --tw-space-y-reverse: 0;
+  margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
+  margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
+}
+
 .space-y-2 > :not([hidden]) ~ :not([hidden]) {
   --tw-space-y-reverse: 0;
   margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
@@ -2229,6 +2293,11 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   padding: 2rem;
 }
 
+.px-1 {
+  padding-left: 0.25rem;
+  padding-right: 0.25rem;
+}
+
 .px-2 {
   padding-left: 0.5rem;
   padding-right: 0.5rem;
@@ -2401,6 +2470,10 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg {
   color: var(--accent);
 }
 
+.text-accent2 {
+  color: var(--accent2);
+}
+
 .text-bg {
   color: var(--bg);
 }
@@ -3650,6 +3723,10 @@ article.toast.border-border\/30.rounded-lg.overflow-hidden.group.bg-bg {
   color: var(--accent);
 }
 
+.hover\:text-accent2:hover {
+  color: var(--accent2);
+}
+
 .hover\:text-text:hover {
   color: var(--text);
 }
@@ -3698,6 +3775,10 @@ article.toast.border-border\/30.rounded-lg.overflow-hidden.group.bg-bg {
   color: var(--accent);
 }
 
+.hover\:text-accent2:hover {
+  color: var(--accent2);
+}
+
 .hover\:text-text:hover {
   color: var(--text);
 }
diff --git a/themes/danix-xyz-hacker/assets/js/fortune.js b/themes/danix-xyz-hacker/assets/js/fortune.js
new file mode 100644 (file)
index 0000000..d4f981b
--- /dev/null
@@ -0,0 +1,9 @@
+(function() {
+  const el = document.getElementById('fortune-quote');
+  if (!el) return;
+  const quotes = JSON.parse(el.dataset.quotes);
+  if (!quotes || quotes.length === 0) return;
+  const q = quotes[Math.floor(Math.random() * quotes.length)];
+  el.querySelector('.fortune-text').textContent = '"' + q.text + '"';
+  el.querySelector('.fortune-author').textContent = 'β€” ' + q.author;
+})();
index c649e84e62d5c662cba9f3617b8cda60306bfd73..006714be7637535caab633eac737575b1fbb4215 100644 (file)
@@ -1,38 +1,81 @@
+{{- $quotes := .Site.Data.quotes.quotes -}}
+
 <footer class="mt-16 frosted-bar border-t py-12 relative z-20">
   <div class="container mx-auto px-4">
-    <div class="grid md:grid-cols-3 gap-8 mb-8">
-      <!-- About -->
+    <div class="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
+
+      <!-- Column 1: Fortune Cookie -->
       <div>
-        <h3 class="font-bold text-accent mb-3 font-oxanium">{{ .Site.Title }}</h3>
-        <p class="text-sm text-text-dim">{{ .Site.Params.siteDescription }}</p>
+        <p class="font-mono text-xs text-text-dim mb-2">$ fortune danix</p>
+        <div id="fortune-quote" aria-live="polite" data-quotes='{{ $quotes | jsonify }}'>
+          <blockquote>
+            <p class="fortune-text font-mono text-sm text-text italic leading-relaxed">
+              "{{ (index $quotes 0).text }}"
+            </p>
+            <cite class="fortune-author font-mono text-xs text-text-dim not-italic mt-2 block">
+              β€” {{ (index $quotes 0).author }}
+            </cite>
+          </blockquote>
+        </div>
       </div>
 
-      <!-- Quick links -->
+      <!-- Column 2: About (Terminal Readout) -->
       <div>
-        <h4 class="font-semibold text-accent mb-3">{{ i18n "links" }}</h4>
-        <ul class="space-y-2">
-          {{ range .Site.Menus.main }}
-            <li>
-              <a href="{{ .URL }}" class="text-sm text-text-dim hover:text-accent transition-colors">
-                {{ i18n .Name }}
-              </a>
-            </li>
-          {{ end }}
-        </ul>
+        <dl class="space-y-1">
+          <div class="flex gap-2">
+            <dt class="text-text-dim font-mono text-xs w-20 shrink-0">role:</dt>
+            <dd class="text-text font-mono text-xs">Cybersecurity Specialist</dd>
+          </div>
+          <div class="flex gap-2">
+            <dt class="text-text-dim font-mono text-xs w-20 shrink-0">cert:</dt>
+            <dd class="text-accent2 font-mono text-xs font-semibold">eJPT</dd>
+          </div>
+          <div class="flex gap-2">
+            <dt class="text-text-dim font-mono text-xs w-20 shrink-0">os:</dt>
+            <dd class="text-text font-mono text-xs">Slackware <span class="text-text-dim">(2005–present)</span></dd>
+          </div>
+          <div class="flex gap-2">
+            <dt class="text-text-dim font-mono text-xs w-20 shrink-0">focus:</dt>
+            <dd class="text-text font-mono text-xs">open-source Β· privacy</dd>
+          </div>
+        </dl>
       </div>
 
-      <!-- Social (if configured) -->
+      <!-- Column 3: Stack & Feature Badges -->
       <div>
-        <h4 class="font-semibold text-accent mb-3">{{ i18n "contact" }}</h4>
-        <a href="mailto:{{ .Site.Params.email }}" class="text-sm text-text-dim hover:text-accent transition-colors">
-          {{ i18n "email" }}: {{ .Site.Params.email }}
-        </a>
+        <p class="text-text-dim font-mono text-xs mb-1">{{ i18n "footer_built_with" }}</p>
+        <div class="flex flex-wrap gap-1.5 mb-3">
+          <span class="badge-footer-accent">Hugo</span>
+          <span class="badge-footer-accent">Tailwind CSS</span>
+          <span class="badge-footer-accent">Alpine.js</span>
+          <span class="badge-footer-accent">HTML5</span>
+          <span class="badge-footer-accent">CSS3</span>
+          <span class="badge-footer-accent">JavaScript</span>
+        </div>
+
+        <p class="text-text-dim font-mono text-xs mb-1">{{ i18n "footer_features" }}</p>
+        <div class="flex flex-wrap gap-1.5">
+          <span class="badge-footer-accent2">WCAG 2.1 AA</span>
+          <span class="badge-footer-accent2">Open Source</span>
+          <span class="badge-footer-accent2">Privacy Friendly</span>
+          <span class="badge-footer-accent2">Claude Code</span>
+        </div>
       </div>
     </div>
 
-    <!-- Copyright -->
-    <div class="pt-8 border-t border-border text-center text-xs text-text-dim">
+    <!-- Copyright Bar -->
+    <div class="pt-8 border-t border-border text-center text-xs text-text-dim space-y-1">
+      <p>
+        Made with <span aria-hidden="true">❀️</span><span class="sr-only">love</span>,
+        lack of <span aria-hidden="true">😴</span><span class="sr-only">sleep</span>,
+        lots of <span aria-hidden="true">β˜•</span><span class="sr-only">coffee</span>
+        by <a href="{{ .Site.LanguagePrefix }}/is/" class="text-accent hover:text-accent2 transition-colors py-2 px-1">danix</a>
+      </p>
       <p>&copy; {{ now.Year }} {{ .Site.Params.author }}. {{ i18n "allRightsReserved" }}</p>
     </div>
   </div>
+
+  <!-- Fortune.js: Pick a random quote on each page load -->
+  {{- $fortuneJS := resources.Get "js/fortune.js" | minify -}}
+  <script src="{{ $fortuneJS.RelPermalink }}"></script>
 </footer>