Adding contact information, updating about page

This commit is contained in:
Dan 2026-01-06 14:54:30 +00:00
parent 80b1d7634a
commit 0041a48bae
11 changed files with 526 additions and 43 deletions

View file

@ -2,6 +2,10 @@
color: white; color: white;
margin: auto; margin: auto;
.content-screen {
position: relative !important;
}
> .about-content { > .about-content {
width: 50%; width: 50%;
margin: auto; margin: auto;
@ -14,6 +18,125 @@
grid-column: 1 / -1; grid-column: 1 / -1;
} }
.about-contact-section {
margin-top: 1rem;
p {
margin-bottom: 1.5rem;
line-height: 1.6;
}
.contact-info {
display: flex;
flex-direction: column;
gap: 1rem;
}
.contact-item {
display: flex;
flex-direction: column;
gap: 0.5rem;
@media (min-width: 600px) {
flex-direction: row;
align-items: center;
flex-wrap: wrap;
}
}
.contact-label {
font-weight: bold;
white-space: nowrap;
}
.info {
color: #0f0;
text-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
text-decoration: none;
border-bottom: 1px dotted rgba(0, 255, 0, 0.5);
transition: all 0.3s ease;
&:hover {
border-bottom-style: solid;
border-bottom-color: rgba(0, 255, 0, 0.8);
text-shadow: 0 0 10px rgba(0, 255, 0, 0.8);
}
}
.pgp-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-left: 0;
@media (min-width: 600px) {
margin-left: 0.5rem;
}
}
.pgp-button {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 10px;
background: rgba(0, 255, 0, 0.1);
border: 1px solid rgba(0, 255, 0, 0.3);
color: #0f0;
text-decoration: none;
font-family: monospace;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.3s ease;
text-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
border-radius: 3px;
.button-icon {
font-size: 1em;
line-height: 1;
}
&:hover {
background: rgba(0, 255, 0, 0.2);
border-color: rgba(0, 255, 0, 0.6);
box-shadow: 0 0 10px rgba(0, 255, 0, 0.4);
text-shadow: 0 0 10px rgba(0, 255, 0, 0.8);
}
&:active {
transform: translateY(1px);
}
}
.copy-feedback {
margin-top: 0.5rem;
font-size: 0.85rem;
padding: 6px 10px;
border-radius: 3px;
opacity: 0;
transform: translateY(-10px);
transition: all 0.3s ease;
&.show {
opacity: 1;
transform: translateY(0);
}
&.success {
color: #0f0;
background: rgba(0, 255, 0, 0.1);
border: 1px solid rgba(0, 255, 0, 0.3);
text-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
}
&.error {
color: #f00;
background: rgba(255, 0, 0, 0.1);
border: 1px solid rgba(255, 0, 0, 0.3);
text-shadow: 0 0 5px rgba(255, 0, 0, 0.5);
}
}
}
.about-header { .about-header {
position: absolute; position: absolute;
left: -80px; left: -80px;
@ -37,6 +160,10 @@
} }
} }
> .about-container {
position: relative;
}
> .info-badges { > .info-badges {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
@ -55,6 +182,23 @@
transform: rotate(5deg); transform: rotate(5deg);
} }
} }
.about-music {
pointer-events: none;
> .music {
scale: 1.6;
.ipod-group {
top: 50%;
left: 25%;
}
.vu-meter {
position: relative;
}
}
}
} }
} }

View file

@ -558,6 +558,142 @@
} }
} }
// Contact section styling
.blog-contact-section {
margin-top: 40px;
padding: 20px;
background: rgba(0, 255, 0, 0.05);
border: 1px solid rgba(0, 255, 0, 0.3);
border-radius: 4px;
@include media-down(lg) {
margin-top: 30px;
padding: 15px;
}
.contact-title {
font-size: 1.3rem;
margin-bottom: 15px;
color: greenyellow;
text-shadow: 0 0 10px rgba(173, 255, 47, 0.5);
@include media-down(lg) {
font-size: 1.1rem;
}
}
.contact-content {
display: flex;
flex-direction: column;
gap: 15px;
}
.contact-email,
.contact-pgp {
font-size: 0.95rem;
line-height: 1.6;
@include media-down(lg) {
font-size: 0.9rem;
}
}
.contact-label {
color: greenyellow;
font-weight: bold;
margin-right: 10px;
text-shadow: 0 0 5px rgba(173, 255, 47, 0.5);
}
.contact-email a {
color: #0f0;
text-decoration: none;
border-bottom: 1px dotted rgba(0, 255, 0, 0.5);
transition: all 0.3s ease;
&:hover {
border-bottom-style: solid;
border-bottom-color: rgba(0, 255, 0, 0.8);
text-shadow: 0 0 10px rgba(0, 255, 0, 0.8);
background: rgba(0, 255, 0, 0.05);
}
}
.pgp-actions {
display: flex;
gap: 10px;
margin-top: 10px;
flex-wrap: wrap;
}
.pgp-button {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 15px;
background: rgba(0, 255, 0, 0.1);
border: 1px solid rgba(0, 255, 0, 0.3);
color: #0f0;
text-decoration: none;
font-family: monospace;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.3s ease;
text-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
border-radius: 3px;
@include media-down(lg) {
font-size: 0.8rem;
padding: 6px 12px;
}
.button-icon {
font-size: 1.1em;
line-height: 1;
}
&:hover {
background: rgba(0, 255, 0, 0.2);
border-color: rgba(0, 255, 0, 0.6);
box-shadow: 0 0 10px rgba(0, 255, 0, 0.4);
text-shadow: 0 0 10px rgba(0, 255, 0, 0.8);
}
&:active {
transform: translateY(1px);
}
}
.copy-feedback {
margin-top: 10px;
font-size: 0.85rem;
padding: 8px 12px;
border-radius: 3px;
opacity: 0;
transform: translateY(-10px);
transition: all 0.3s ease;
&.show {
opacity: 1;
transform: translateY(0);
}
&.success {
color: #0f0;
background: rgba(0, 255, 0, 0.1);
border: 1px solid rgba(0, 255, 0, 0.3);
text-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
}
&.error {
color: #f00;
background: rgba(255, 0, 0, 0.1);
border: 1px solid rgba(255, 0, 0, 0.3);
text-shadow: 0 0 5px rgba(255, 0, 0, 0.5);
}
}
}
// Blog image styling // Blog image styling
.blog-img-container { .blog-img-container {
position: relative; position: relative;

View file

@ -16,7 +16,7 @@
.whiteboard { .whiteboard {
background: linear-gradient(135deg, #f5f5f0 0%, #e8e8dd 100%); background: linear-gradient(135deg, #f5f5f0 0%, #e8e8dd 100%);
border-radius: 8px; border-radius: 8px;
padding: 3rem 2rem;
box-shadow: box-shadow:
0 10px 40px rgba(0, 0, 0, 0.1), 0 10px 40px rgba(0, 0, 0, 0.1),
inset 0 0 100px rgba(0, 0, 0, 0.02); inset 0 0 100px rgba(0, 0, 0, 0.02);
@ -24,15 +24,27 @@
// Subtle texture overlay // Subtle texture overlay
&::before { &::before {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background-image: background-image:
repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0, 0, 0, 0.01) 2px, rgba(0, 0, 0, 0.01) 4px), repeating-linear-gradient(
repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(0, 0, 0, 0.01) 2px, rgba(0, 0, 0, 0.01) 4px); 0deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.01) 2px,
rgba(0, 0, 0, 0.01) 4px
),
repeating-linear-gradient(
90deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.01) 2px,
rgba(0, 0, 0, 0.01) 4px
);
pointer-events: none; pointer-events: none;
border-radius: 8px; border-radius: 8px;
} }
@ -77,18 +89,20 @@
.resource-pin { .resource-pin {
position: relative; position: relative;
transform: rotate(0deg); transform: rotate(0deg);
transition: transform 0.3s ease, box-shadow 0.3s ease; transition:
transform 0.3s ease,
box-shadow 0.3s ease;
// Random slight rotations for pins // Random slight rotations for pins
&:nth-child(3n+1) { &:nth-child(3n + 1) {
transform: rotate(-1deg); transform: rotate(-1deg);
} }
&:nth-child(3n+2) { &:nth-child(3n + 2) {
transform: rotate(1deg); transform: rotate(1deg);
} }
&:nth-child(3n+3) { &:nth-child(3n + 3) {
transform: rotate(-0.5deg); transform: rotate(-0.5deg);
} }
@ -120,7 +134,7 @@
transition: transform 0.3s ease; transition: transform 0.3s ease;
&::after { &::after {
content: ''; content: "";
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -147,19 +161,19 @@
gap: 1rem; gap: 1rem;
// Vary note colors // Vary note colors
.resource-pin:nth-child(4n+1) & { .resource-pin:nth-child(4n + 1) & {
background: linear-gradient(135deg, #fef9c3 0%, #fde68a 100%); // Yellow background: linear-gradient(135deg, #fef9c3 0%, #fde68a 100%); // Yellow
} }
.resource-pin:nth-child(4n+2) & { .resource-pin:nth-child(4n + 2) & {
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); // Blue background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); // Blue
} }
.resource-pin:nth-child(4n+3) & { .resource-pin:nth-child(4n + 3) & {
background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%); // Green background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%); // Green
} }
.resource-pin:nth-child(4n+4) & { .resource-pin:nth-child(4n + 4) & {
background: linear-gradient(135deg, #fce7f3 0%, #fbcfe8 100%); // Pink background: linear-gradient(135deg, #fce7f3 0%, #fbcfe8 100%); // Pink
} }
} }
@ -172,7 +186,13 @@
// Lavalamp icon // Lavalamp icon
&.lavalamp { &.lavalamp {
background: linear-gradient(180deg, transparent 0%, transparent 20%, #9333ea 20%, #7c3aed 100%); background: linear-gradient(
180deg,
transparent 0%,
transparent 20%,
#9333ea 20%,
#7c3aed 100%
);
border-radius: 10px 10px 40px 40px; border-radius: 10px 10px 40px 40px;
border: 3px solid #6b21a8; border: 3px solid #6b21a8;
position: relative; position: relative;
@ -180,7 +200,7 @@
// Lamp base // Lamp base
&::before { &::before {
content: ''; content: "";
position: absolute; position: absolute;
bottom: -8px; bottom: -8px;
left: 50%; left: 50%;
@ -193,7 +213,7 @@
// Lava blobs // Lava blobs
&::after { &::after {
content: ''; content: "";
position: absolute; position: absolute;
top: 30%; top: 30%;
left: 30%; left: 30%;
@ -219,7 +239,7 @@
// Equalizer bars // Equalizer bars
&::before, &::before,
&::after { &::after {
content: ''; content: "";
position: absolute; position: absolute;
bottom: 15px; bottom: 15px;
width: 8px; width: 8px;
@ -280,7 +300,8 @@
} }
@keyframes float-blob { @keyframes float-blob {
0%, 100% { 0%,
100% {
transform: translateY(0) scale(1); transform: translateY(0) scale(1);
} }
50% { 50% {
@ -289,7 +310,8 @@
} }
@keyframes float-blob-2 { @keyframes float-blob-2 {
0%, 100% { 0%,
100% {
transform: translateY(0) scale(1); transform: translateY(0) scale(1);
} }
50% { 50% {
@ -298,7 +320,8 @@
} }
@keyframes equalizer-1 { @keyframes equalizer-1 {
0%, 100% { 0%,
100% {
height: 35px; height: 35px;
} }
50% { 50% {
@ -307,7 +330,8 @@
} }
@keyframes equalizer-2 { @keyframes equalizer-2 {
0%, 100% { 0%,
100% {
height: 25px; height: 25px;
} }
50% { 50% {
@ -316,7 +340,8 @@
} }
@keyframes equalizer-3 { @keyframes equalizer-3 {
0%, 100% { 0%,
100% {
height: 30px; height: 30px;
} }
50% { 50% {

View file

@ -6,7 +6,7 @@
aspect-ratio: 300 / 245; aspect-ratio: 300 / 245;
background: linear-gradient(145deg, #b8b8b0, #989888); background: linear-gradient(145deg, #b8b8b0, #989888);
box-shadow: box-shadow:
0 8px 20px rgba(0, 0, 0, 0.7), 0 8px 20px #000000b3,
inset 0 2px 4px rgba(255, 255, 255, 0.3), inset 0 2px 4px rgba(255, 255, 255, 0.3),
inset 0 -2px 4px rgba(0, 0, 0, 0.3); inset 0 -2px 4px rgba(0, 0, 0, 0.3);
padding: 6px 8px 18px 8px; padding: 6px 8px 18px 8px;
@ -20,7 +20,7 @@
&::before { &::before {
content: " "; content: " ";
display: block; display: block;
position: fixed; position: absolute;
top: 6px; top: 6px;
left: 8px; left: 8px;
bottom: 18px; bottom: 18px;
@ -44,7 +44,7 @@
&::after { &::after {
content: " "; content: " ";
display: block; display: block;
position: fixed; position: absolute;
top: 6px; top: 6px;
left: 8px; left: 8px;
bottom: 18px; bottom: 18px;

View file

@ -12,6 +12,10 @@ footer[role="contentinfo"] {
border-left: 1px solid #0f0; border-left: 1px solid #0f0;
border-top-left-radius: 5px; border-top-left-radius: 5px;
> .crt {
position: relative;
}
@include media-down(lg) { @include media-down(lg) {
display: none; display: none;
} }

View file

@ -43,6 +43,10 @@
justify-content: space-around; justify-content: space-around;
height: 100%; height: 100%;
gap: 2px; gap: 2px;
&.crt {
position: relative;
}
} }
.vu-bar { .vu-bar {

View file

@ -103,6 +103,7 @@ body {
pointer-events: none; pointer-events: none;
} }
.crt { .crt {
position: absolute;
animation: textShadow 1.6s infinite; animation: textShadow 1.6s infinite;
background: black; background: black;
color: greenyellow; color: greenyellow;
@ -813,7 +814,6 @@ body {
text-decoration: none !important; text-decoration: none !important;
} }
/* Desk-mounted CRT - on desk left side */ /* Desk-mounted CRT - on desk left side */
.desk-monitor { .desk-monitor {
bottom: 10%; bottom: 10%;
@ -1036,8 +1036,6 @@ body {
} }
} }
@keyframes pulse-slow { @keyframes pulse-slow {
0%, 0%,
100% { 100% {

View file

@ -55,7 +55,7 @@ Zooming in on 2019 onwards makes it clearer:
StackOverflow's real decline starts in 2021, well before anyone gave a shit about AI coding. From January 2021 (140,009 questions) to December 2022 (96,767 questions), it lost 31% of its traffic while "AI coding" searches sat at baseline. StackOverflow's real decline starts in 2021, well before anyone gave a shit about AI coding. From January 2021 (140,009 questions) to December 2022 (96,767 questions), it lost 31% of its traffic while "AI coding" searches sat at baseline.
The AI coding surge doesn't really kick off until early 2023, then explodes through 2025, peaking in August. But by then StackOverflow was already down to 5,885 questions per month. The AI coding surge doesn't really kick off until early 2023, then explodes through 2025, peaking in August. By then StackOverflow was already down to 5,885 questions per month.
## Did the rise of GitHub contribute? ## Did the rise of GitHub contribute?
@ -90,7 +90,7 @@ Could it be a contributing factor? Absolutely. If you had a choice between askin
To actually test this properly you'd need to pull years of StackOverflow comments and run sentiment analysis to see if moderation got worse over time. That's way more effort than I'm putting into this blog post. Maybe one for another day. To actually test this properly you'd need to pull years of StackOverflow comments and run sentiment analysis to see if moderation got worse over time. That's way more effort than I'm putting into this blog post. Maybe one for another day.
## So what's actually going on? ## So what's was the actual cause?
The data suggests AI accelerated something that was already happening. A few things probably contributed: The data suggests AI accelerated something that was already happening. A few things probably contributed:
@ -102,13 +102,13 @@ The data suggests AI accelerated something that was already happening. A few thi
**The collapse (2025-2026)**: By mid-2025, AI coding tools are just... everywhere. GitHub Copilot, ChatGPT, Claude, all of them baked into every IDE and workflow. The August 2025 peak in "AI coding" searches lines up with StackOverflow hitting 5,885 questions. That's a 96% decline from five years earlier. **The collapse (2025-2026)**: By mid-2025, AI coding tools are just... everywhere. GitHub Copilot, ChatGPT, Claude, all of them baked into every IDE and workflow. The August 2025 peak in "AI coding" searches lines up with StackOverflow hitting 5,885 questions. That's a 96% decline from five years earlier.
## The bottom line ## Summary
AI didn't kill StackOverflow, but it's definitely finishing the job. The platform was already bleeding out when ChatGPT showed up: content saturation, better tooling, the ecosystem maturing. But AI coding tools changed the game completely. Why search through old forum posts when you can just ask? AI didn't kill StackOverflow, but it's definitely finishing the job. The platform was already bleeding out when ChatGPT showed up: content saturation, better tooling, the ecosystem maturing. But AI coding tools changed the game completely. Why search through old forum posts when you can just ask?
The inverse relationship is stark: as AI coding interest hits its peak in 2025, StackOverflow craters. By December 2025 we're at 3,862 questions. That's roughly the same as September 2008, just a few months after the platform launched. The inverse relationship is stark: as AI coding interest hits its peak in 2025, StackOverflow craters. By December 2025 we're at 3,862 questions. That's roughly the same as September 2008, just a few months after the platform launched.
Twelve years to build it. Five years to tear it down. And yeah, the last three were almost certainly AI finishing what was already started. Twelve years to build it. Five years to watch it end. The last three were almost certainly AI finishing what was already started.
### Footnote ### Footnote
@ -116,7 +116,7 @@ This is the hardest and longest I have thought about anything in a long time. I
The CSV data for this post can be found on my [GitHub](https://github.com/unbolt/ritual.sh/tree/main/content/blog/the-downfall-of-stackoverflow). The CSV data for this post can be found on my [GitHub](https://github.com/unbolt/ritual.sh/tree/main/content/blog/the-downfall-of-stackoverflow).
I hope you found this interesting. Rock on. I hope you found this interesting. Please get in touch if you have any further insights.
--- ---
@ -135,5 +135,3 @@ I hope you found this interesting. Rock on.
[^5]: [Comment](https://news.ycombinator.com/item?id=46482624) [^5]: [Comment](https://news.ycombinator.com/item?id=46482624)
[^6]: In 2013, Tim Schreiber wrote about ["StackOverlords"](https://timschreiber.com/2013/10/30/beware-the-stackoverlords/) who "ruthlessly wield" their privileges against newcomers. By 2018, StackOverflow officially acknowledged the problem in their blog post ["Stack Overflow Isn't Very Welcoming. It's Time for That to Change."](https://stackoverflow.blog/2018/04/26/stack-overflow-isnt-very-welcoming-its-time-for-that-to-change/) [^6]: In 2013, Tim Schreiber wrote about ["StackOverlords"](https://timschreiber.com/2013/10/30/beware-the-stackoverlords/) who "ruthlessly wield" their privileges against newcomers. By 2018, StackOverflow officially acknowledged the problem in their blog post ["Stack Overflow Isn't Very Welcoming. It's Time for That to Change."](https://stackoverflow.blog/2018/04/26/stack-overflow-isnt-very-welcoming-its-time-for-that-to-change/)

View file

@ -1,17 +1,15 @@
{{ define "header" }}{{ partial "page-header.html" . }}{{ end }} {{ define {{ define "main" }}
"main" }}
<article class="about-page"> <article class="about-page">
<div class="about-content"> <div class="about-content">
<div class="content-screen"> <div class="content-screen">
<div class="about-header">{{ partial "elements/lavalamp.html" . }}</div> <div class="about-header">{{ partial "elements/lavalamp.html" . }}</div>
<div class="screen-display crt no-scroll"> <div class="screen-display crt no-scroll">
> about -v <br /> > about -s <br />
<span>Name</span> / <span class="info">Dan (He/Him)</span><br /> <span>Name</span> / <span class="info">Dan (He/Him)</span><br />
<span>Age</span> / <span class="info">40-something</span><br /> <span>Age</span> / <span class="info">40-something</span><br />
<span>Location</span> / <span class="info">UK 🇬🇧</span><br /><br /> <span>Location</span> / <span class="info">UK 🇬🇧</span><br /><br />
<span>Interests</span> / <span>Interests</span> /
<span class="info"> <span class="info"> Programming. Music. Movies. Tech. Photography.</span
Programming. Music. Movies. Tech. Photography. </span
><br /> ><br />
<span class="cursor-blink">_</span> <span class="cursor-blink">_</span>
</div> </div>
@ -23,6 +21,16 @@
partial "elements/lcd-screen.html" (dict "text" .) }} {{ end }} partial "elements/lcd-screen.html" (dict "text" .) }} {{ end }}
</div> </div>
<div class="wide-item">
<div class="content-screen">
<div class="screen-display crt">
> cat manifesto
<span class="cursor-blink">_</span>
</div>
</div>
</div>
<div class="wide-item manifesto-container"> <div class="wide-item manifesto-container">
<div class="content-screen"> <div class="content-screen">
<div class="screen-display crt"> <div class="screen-display crt">
@ -105,10 +113,96 @@
><br /> ><br />
Camera / <span class="info">Fuji X-T5</span><br /> Camera / <span class="info">Fuji X-T5</span><br />
Audio / <span class="info">Hiby R4 EVA, Fiio FT-1</span> <br /><br /> Audio / <span class="info">Hiby R4 EVA, Fiio FT-1</span> <br /><br />
More info coming soon... Check out my <a href="/now">/now</a> page to see more hardware and
<br /><span class="cursor-blink">_</span> software information. <br /><span class="cursor-blink">_</span>
</div>
</div>
<div class="about-music hidden-xxl-down">
<div class="music">
{{ partial "elements/ipod.html" . }}
<div class="">{{ partial "elements/vu-meter.html" . }}</div>
</div>
</div>
<div class="content-screen wide-item">
<div class="screen-display crt">
> cat contact_info<br />
<div class="about-contact-section">
<p>
If you have any comments, questions, corrections, or just fancy
saying "hello" please feel free to get in touch.
</p>
<p>
I am still debating with myself if I want to put more effort into a
mastodon/bluesky type thing, so for now email is the best way to
talk to me. PGP is available if that's your kind of thing.
</p>
<div class="contact-info">
<div class="contact-item">
<span class="contact-label">Email</span> /
<a href="mailto:dan@ritual.sh" class="info">dan@ritual.sh</a>
</div>
<div class="contact-item">
<span class="contact-label">PGP Public Key</span> /
<div class="pgp-actions">
<a href="/publickey.asc" download class="pgp-button">
<span class="button-icon"></span> Download
</a>
<button id="copy-pgp-key-about" class="pgp-button">
<span class="button-icon"></span> Copy to Clipboard
</button>
</div>
</div>
<div id="copy-feedback-about" class="copy-feedback"></div>
</div>
</div>
<pre>
| .-.
| / \ .-.
| / \ / \ .-. .-. _ _
+--/-------\-----/-----\-----/---\---/---\---/-\-/-\/\/---
| / \ / \ / '-' '-'
|/ '-' '-'
</pre>
<span class="cursor-blink">_</span>
</div> </div>
</div> </div>
</div> </div>
</article> </article>
<script>
document.addEventListener("DOMContentLoaded", function () {
const copyButton = document.getElementById("copy-pgp-key-about");
const feedback = document.getElementById("copy-feedback-about");
if (copyButton) {
copyButton.addEventListener("click", async function () {
try {
const response = await fetch("/publickey.asc");
const pgpKey = await response.text();
await navigator.clipboard.writeText(pgpKey);
feedback.textContent = "PGP key copied to clipboard!";
feedback.classList.add("show", "success");
setTimeout(() => {
feedback.classList.remove("show");
}, 3000);
} catch (err) {
feedback.textContent = "Failed to copy key";
feedback.classList.add("show", "error");
setTimeout(() => {
feedback.classList.remove("show");
}, 3000);
}
});
}
});
</script>
{{ end }} {{ end }}

View file

@ -23,6 +23,37 @@
<div class="blog-summary">{{ .Content }}</div> <div class="blog-summary">{{ .Content }}</div>
</article> </article>
<section class="blog-contact-section">
<h2 class="contact-title">Contact</h2>
<div class="contact-content">
<p>
If you found this interesting, have any comments, questions,
corrections, or just fancy saying "hello" please feel free to get
in touch.
</p>
<div class="contact-email">
<span class="contact-label">Email:</span>
<a href="mailto:dan@ritual.sh">dan@ritual.sh</a>
</div>
<div class="contact-pgp">
<span class="contact-label">PGP Public Key:</span>
<div class="pgp-actions">
<a
href="/publickey.asc"
download
class="pgp-button download-key"
>
<span class="button-icon"></span> Download
</a>
<button id="copy-pgp-key" class="pgp-button copy-key">
<span class="button-icon"></span> Copy to Clipboard
</button>
</div>
<div id="copy-feedback" class="copy-feedback"></div>
</div>
</div>
</section>
<nav class="blog-post-navigation"> <nav class="blog-post-navigation">
<div class="post-nav-links"> <div class="post-nav-links">
{{ with .PrevInSection }} {{ with .PrevInSection }}
@ -49,4 +80,36 @@
<script src="/js/footnote-scroll.js"></script> <script src="/js/footnote-scroll.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
const copyButton = document.getElementById("copy-pgp-key");
const feedback = document.getElementById("copy-feedback");
if (copyButton) {
copyButton.addEventListener("click", async function () {
try {
const response = await fetch("/publickey.asc");
const pgpKey = await response.text();
await navigator.clipboard.writeText(pgpKey);
feedback.textContent = "PGP key copied to clipboard!";
feedback.classList.add("show", "success");
setTimeout(() => {
feedback.classList.remove("show");
}, 3000);
} catch (err) {
feedback.textContent = "Failed to copy key";
feedback.classList.add("show", "error");
setTimeout(() => {
feedback.classList.remove("show");
}, 3000);
}
});
}
});
</script>
{{ end }} {{ end }}

17
static/publickey.asc Normal file
View file

@ -0,0 +1,17 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
xjMEaVzgdxYJKwYBBAHaRw8BAQdADvLcBAhz3La0tovwPlJ2Z5uKHufb9MS9
6CGKBIgssOfNHWRhbkByaXR1YWwuc2ggPGRhbkByaXR1YWwuc2g+wsARBBMW
CgCDBYJpXOB3AwsJBwkQhk08tBmPAUJFFAAAAAAAHAAgc2FsdEBub3RhdGlv
bnMub3BlbnBncGpzLm9yZ2lm/k+MdxoNH3+THDnkaLgtXLiV1AuTGyJ0ZlAe
4ngSAxUKCAQWAAIBAhkBApsDAh4BFiEEFv8cu2aJ5iyvXhJ0hk08tBmPAUIA
ABB0AP0ZvUva/yf6ZR8T7Zwp8+RtqyYfiDlJbpqt3hEGALw0EgD6AxO3O4tm
f2IguB2kuUfrH223xGEzIOHYz1Ciwt6haAPOOARpXOB3EgorBgEEAZdVAQUB
AQdAjllaNe/Z1No5rRxVz6SBsSN4o2xDHPWb0PnGxZAsT3EDAQgHwr4EGBYK
AHAFgmlc4HcJEIZNPLQZjwFCRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9w
ZW5wZ3Bqcy5vcmfxjGz++fxlZxuZLgSTOTjWRrtwvPuLEFJSN1VxonqqcQKb
DBYhBBb/HLtmieYsr14SdIZNPLQZjwFCAABdiAD8Cz5dxfHSm1mBwQ0jKjZd
sktUeaa3Ksw2NsMFU3sbnoIBAMBoAaQVq+q5RoLn1ZOT/DIeDU+1o5HVAL0k
sb/b4NYN
=EIiw
-----END PGP PUBLIC KEY BLOCK-----