feat(identity): real author photo across the site + About page rewrite
Deploy / build (push) Successful in 48s

The photo is the identity evidence the external review flagged as the biggest
gap. One source image (Pictures/Professional Portrait with Blue Gradient
Background.png, 1254x1254), two sizes, WebP first with a JPEG fallback —
continuous tone is the case where WebP/JPEG beat PNG:

  640x640  42 KB JPG / 26 KB WebP   About page (190 CSS px)
  128x128   3 KB JPG /  2 KB WebP   author card + home hero avatar

It now appears in the About hero, the author card at the end of every post, the
home hero (both languages), and JSON-LD Person.image — the node search engines
read to associate the name with a face.

About went from 1,024 characters to five sections: identity with photo, "By the
numbers" (162 containers / 3 hosts / 71 essays / 0 hosting companies between me
and the servers), the real tech stack in four columns, the Hoelee Enterprise
block (website design and development is the main business, email hosting
secondary), and a contact split between hiring and business.

Also: the headshot's dark-blue background sits close to the page background, so
the photo, hero avatar and card avatar get a faint brand ring plus shadow.

Pictures/ keeps the converted JPG master next to the untouched PNG.
This commit is contained in:
hoelee committed 2026-10-16 22:07:25 +08:00
1 parent 215051ef08
commit 6b7c5a983f
12 files changed
+410 -65

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+46 -5
View File
@@ -3,21 +3,62 @@ import { SITE } from '../config';
interface Props {
bio?: string;
lang?: 'en' | 'zh';
}
const { bio = 'Full-stack developer & DevOps engineer. I build web apps, self-host infrastructure, and automate things — this blog is my living portfolio.' } = Astro.props;
const {
bio = 'Full-stack developer & DevOps engineer based in Malaysia. I run my own production infrastructure and build web systems for small businesses — this blog is the living record.',
lang = 'en',
} = Astro.props;
const isZh = lang === 'zh';
const t = isZh
? {
hiring: '招聘方:技术栈与履历',
hiringHref: '/zh/about/',
sme: '马来西亚中小企业:网站设计与开发',
smeHref: SITE.website,
stack: '全部技术文章',
stackHref: '/zh/posts/',
}
: {
hiring: 'Hiring? Profile & stack',
hiringHref: '/about/',
sme: 'Malaysian SME? Website design & development',
smeHref: SITE.website,
stack: 'All posts',
stackHref: '/posts/',
};
---
<div class="author-card">
<div class="avatar" aria-hidden="true">{SITE.handle.charAt(0)}</div>
<div>
<p class="who">{SITE.author}</p>
<picture>
<source srcset={SITE.photo.smallWebp} type="image/webp" />
<img
class="avatar"
src={SITE.photo.small}
alt={SITE.author}
width="64"
height="64"
loading="lazy"
decoding="async"
/>
</picture>
<div class="author-body">
<p class="who">{SITE.author} · {SITE.handle}</p>
<p class="bio">{bio}</p>
<div class="links">
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
<a href={SITE.github} target="_blank" rel="noopener">GitHub</a>
<a href={SITE.gitea} target="_blank" rel="noopener">Gitea</a>
<a href={SITE.website} target="_blank" rel="noopener">hoelee.com</a>
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
</div>
<p class="author-split">
<a href={t.hiringHref}>{t.hiring}</a>
<span class="sep">·</span>
<a href={t.smeHref} target="_blank" rel="noopener">{t.sme} ↗</a>
<span class="sep">·</span>
<a href={t.stackHref}>{t.stack}</a>
</p>
</div>
</div>
+10
View File
@@ -17,4 +17,14 @@ export const SITE = {
gitea: 'https://git.hoelee.com/hoelee',
website: 'https://www.hoelee.com',
twitter: '', // add if you have one
// Author photo (B4, 2026-10-16). One real headshot, two sizes, WebP first with the
// JPEG as fallback — continuous-tone images are the case where JPEG/WebP beat PNG.
// Used by: the About page, the author card on every post, the home hero, and the
// JSON-LD `Person.image` (the identity signal search engines read).
photo: {
large: '/images/lee-teong-hoe-640.jpg', // 640x640 — About page (320 CSS px @2x)
largeWebp: '/images/lee-teong-hoe-640.webp',
small: '/images/lee-teong-hoe-128.jpg', // 128x128 — author card avatar (64 CSS px @2x)
smallWebp: '/images/lee-teong-hoe-128.webp',
},
} as const;
+1
View File
@@ -130,6 +130,7 @@ const nav = isZh
alternateName: SITE.handle,
url: SITE.url,
email: SITE.email,
image: new URL(SITE.photo.large, SITE.url).href,
sameAs: [SITE.linkedin, SITE.github, SITE.gitea, SITE.website].filter(Boolean),
jobTitle: 'Full-stack Developer & DevOps Engineer',
// The consultancy that does the client work — ties the blog's author node to
+119 -25
View File
@@ -6,36 +6,130 @@ import { SITE } from '../config';
<BaseLayout
title="About — Mr Hoelee"
description={`About ${SITE.author} (${SITE.handle}).`}
description={`${SITE.author} (${SITE.handle}) — full-stack developer and DevOps engineer in Malaysia. Runs 162 containers across three self-managed hosts and builds web systems for small businesses.`}
switchHref="/zh/about/"
altLocaleUrl={`${SITE.url}/zh/about/`}
>
<h1>About</h1>
<section class="about-hero">
<picture>
<source srcset={SITE.photo.largeWebp} type="image/webp" />
<img
class="about-photo"
src={SITE.photo.large}
alt={`${SITE.author} (${SITE.handle})`}
width="320"
height="320"
decoding="async"
/>
</picture>
<div>
<h1>{SITE.author} <span class="handle">({SITE.handle})</span></h1>
<p class="about-lede">
Full-stack developer and DevOps engineer in Malaysia. I run the production
infrastructure behind my own and my clients' systems —
<strong>162 containers across three self-managed hosts</strong> — and I build
web systems for Malaysian small businesses.
</p>
<p class="about-links">
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
<span class="sep">·</span>
<a href={SITE.github} target="_blank" rel="noopener">GitHub</a>
<span class="sep">·</span>
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
<span class="sep">·</span>
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
</p>
<p class="about-open">Open to <strong>remote / hybrid</strong> engineering roles.</p>
</div>
</section>
<h2>By the numbers</h2>
<div class="proof-grid">
<div class="proof-card"><span class="n">162</span><span class="l">containers in production</span></div>
<div class="proof-card"><span class="n">3</span><span class="l">self-managed hosts (78 compose stacks)</span></div>
<div class="proof-card"><span class="n">71</span><span class="l">write-ups, each in English and 中文</span></div>
<div class="proof-card"><span class="n">0</span><span class="l">hosting companies between me and the servers</span></div>
</div>
<p>
I'm {SITE.author} ({SITE.handle}), a full-stack developer and DevOps engineer
based in Malaysia. I build web applications — PHP/CodeIgniter, Java/Spring,
JavaScript/TypeScript/React — and self-host a ~140-container homelab
(Docker, Traefik, nginx, Cloudflare tunnels). I run Hoelee Enterprise, a
website design & development business for Malaysian SMEs (email hosting is
a side offering, not the focus).
</p>
<p>
This blog is my living portfolio: I document what I build, from self-hosted
CI/CD and Docker orchestration to Web3 experiments and AI automation. If
you're a recruiter, a client, or a fellow builder, the posts here are the
most honest picture of what I can do.
That footprint is client websites, a mail platform, CI/CD, workflow automation,
analytics and this blog — held together by hand. It is also why the posts here
are specific: when something breaks in production, I am the one who finds out,
and the write-up is the record of what the evidence actually said.
</p>
<AuthorCard bio="Full-stack developer & DevOps engineer based in Malaysia. Open to remote / hybrid opportunities." />
<h2>What I actually work with</h2>
<div class="stack-grid">
<div>
<h3>Infrastructure &amp; DevOps</h3>
<ul>
<li>Docker &amp; Compose, Traefik, nginx, Cloudflare tunnels</li>
<li>Gitea Actions (self-hosted CI/CD), act_runner</li>
<li>Prometheus, Grafana, cAdvisor, Uptime Kuma</li>
<li>Unraid, Synology DSM, Linux (Debian/Ubuntu/Alpine)</li>
</ul>
</div>
<div>
<h3>Backend &amp; automation</h3>
<ul>
<li>PHP / CodeIgniter, Java / Spring</li>
<li>Node.js / TypeScript, Python</li>
<li>n8n, NocoDB, PostgreSQL / MySQL</li>
<li>REST APIs, webhooks, browser automation (CDP/Playwright)</li>
</ul>
</div>
<div>
<h3>Frontend &amp; web</h3>
<ul>
<li>Astro, JavaScript / TypeScript, React</li>
<li>WordPress (Kadence, Elementor, custom plugins)</li>
<li>Core Web Vitals, accessibility, responsive layout</li>
</ul>
</div>
<div>
<h3>Self-hosted services I run</h3>
<ul>
<li>Authentik SSO, Mailcow, AList, Mem0</li>
<li>Umami analytics, Tor onion services</li>
<li>Whisper/TTS inference on a local GPU</li>
</ul>
</div>
</div>
<h2>Contact</h2>
<p>
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
<span class="sep">·</span>
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
<span class="sep">·</span>
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
<span class="sep">·</span>
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
</p>
<h2>For Malaysian SMEs — Hoelee Enterprise</h2>
<div class="sme-block">
<p>
<strong>Website design and development is my main business</strong> — fast,
maintainable sites for Malaysian small and medium businesses, from the design
through to the hosting, DNS, backups and monitoring underneath it.
<strong>Email hosting</strong> is a secondary offering, not the focus.
</p>
<p>
Because I run the infrastructure myself, you get one person who can answer for
the whole stack instead of a developer who cannot see the server and a hosting
company that cannot read the code.
</p>
<p>
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
<span class="sep">·</span>
<a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a>
</p>
</div>
<h2>Get in touch</h2>
<div class="contact-split">
<div>
<h3>For hiring &amp; engineering roles</h3>
<p><a href={`mailto:${SITE.email}`}>{SITE.email}</a></p>
<p><a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a> · <a href={SITE.github} target="_blank" rel="noopener">GitHub</a></p>
<p>Remote or hybrid; available for a technical conversation any time.</p>
</div>
<div>
<h3>For business &amp; web projects</h3>
<p><a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a></p>
<p><a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a></p>
<p>Malaysian SMEs: website design &amp; development, hosting and maintenance.</p>
</div>
</div>
<AuthorCard />
</BaseLayout>
+14 -1
View File
@@ -25,7 +25,20 @@ const featuredPosts = featured.length === 4 ? featured : posts.slice(0, 4);
<BaseLayout title={SITE.title} description={SITE.description} altLocaleUrl={`${SITE.url}/zh/`}>
<section class="hero">
<h1>{SITE.author} — full-stack developer &amp; DevOps engineer</h1>
<div class="hero-id">
<picture>
<source srcset={SITE.photo.smallWebp} type="image/webp" />
<img
class="hero-photo"
src={SITE.photo.small}
alt={`${SITE.author} (${SITE.handle})`}
width="72"
height="72"
decoding="async"
/>
</picture>
<h1>{SITE.author} — full-stack developer &amp; DevOps engineer</h1>
</div>
<p class="hero-sub">
I run <strong>162 containers across three self-managed hosts</strong> — client sites, a
mail platform, CI/CD, workflow automation and this blog — and I build web systems for
+113 -23
View File
@@ -6,35 +6,125 @@ import { SITE } from '../../config';
<BaseLayout
title="关于 — Mr Hoelee"
description={`关于 ${SITE.author}(${SITE.handle})。`}
description={`${SITE.author}(${SITE.handle})——在马来西亚的全栈开发者兼 DevOps 工程师。独自运维三台服务器上的 162 个容器,为中小企业构建 Web 系统。`}
lang="zh"
switchHref="/about/"
altLocaleUrl={`${SITE.url}/about/`}
>
<h1>关于</h1>
<section class="about-hero">
<picture>
<source srcset={SITE.photo.largeWebp} type="image/webp" />
<img
class="about-photo"
src={SITE.photo.large}
alt={`${SITE.author}(${SITE.handle})`}
width="320"
height="320"
decoding="async"
/>
</picture>
<div>
<h1>{SITE.author} <span class="handle">({SITE.handle})</span></h1>
<p class="about-lede">
在马来西亚的全栈开发者兼 DevOps 工程师。我亲手运维自己和客户系统背后的生产环境——
<strong>三台自管服务器上的 162 个容器</strong>——并为马来西亚中小企业构建 Web 系统。
</p>
<p class="about-links">
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
<span class="sep">·</span>
<a href={SITE.github} target="_blank" rel="noopener">GitHub</a>
<span class="sep">·</span>
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
<span class="sep">·</span>
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
</p>
<p class="about-open">接受 <strong>远程 / 混合</strong> 工程岗位机会。</p>
</div>
</section>
<h2>几个数字</h2>
<div class="proof-grid">
<div class="proof-card"><span class="n">162</span><span class="l">个生产环境容器</span></div>
<div class="proof-card"><span class="n">3</span><span class="l">台自管服务器(78 个 compose 栈)</span></div>
<div class="proof-card"><span class="n">71</span><span class="l">篇长文,每篇中英双版</span></div>
<div class="proof-card"><span class="n">0</span><span class="l">夹在我和服务器之间的托管商</span></div>
</div>
<p>
我是 {SITE.author}({SITE.handle}),一名base在马来西亚的全栈开发者兼 DevOps
工程师。我做 Web 应用——PHP/CodeIgniter、Java/Spring、JavaScript/TypeScript/React——
并自托管一个约 140 个容器的 homelab(Docker、Traefik、nginx、Cloudflare tunnel)。
我运营 Hoelee Enterprise,一家面向马来西亚中小企业的网站设计开发公司
(邮箱托管只是副业,并非主业)。
</p>
<p>
这个博客就是我的动态作品集:我把自己构建的东西记录下来,从自托管 CI/CD、
Docker 编排,到 Web3 实验与 AI 自动化。无论你是招聘方、客户,还是同路的
开发者,这里的文章都是我能做什么最诚实的呈现。
这套环境里有客户网站、邮件平台、CI/CD、流程自动化、统计和这个博客,全靠手工拴在一起。
这也是为什么这里的文章都很具体:生产环境出问题时,第一个知道的人是我,
文章就是「证据到底说了什么」的记录。
</p>
<AuthorCard bio="base在马来西亚的全栈开发者兼 DevOps 工程师。接受远程/混合办公机会。" />
<h2>我实际在用的东西</h2>
<div class="stack-grid">
<div>
<h3>基建与 DevOps</h3>
<ul>
<li>Docker 与 Compose、Traefik、nginx、Cloudflare tunnel</li>
<li>Gitea Actions(自托管 CI/CD)、act_runner</li>
<li>Prometheus、Grafana、cAdvisor、Uptime Kuma</li>
<li>Unraid、Synology DSM、Linux(Debian/Ubuntu/Alpine)</li>
</ul>
</div>
<div>
<h3>后端与自动化</h3>
<ul>
<li>PHP / CodeIgniter、Java / Spring</li>
<li>Node.js / TypeScript、Python</li>
<li>n8n、NocoDB、PostgreSQL / MySQL</li>
<li>REST API、webhook、浏览器自动化(CDP/Playwright)</li>
</ul>
</div>
<div>
<h3>前端与 Web</h3>
<ul>
<li>Astro、JavaScript / TypeScript、React</li>
<li>WordPress(Kadence、Elementor、自写插件)</li>
<li>Core Web Vitals、可访问性、响应式布局</li>
</ul>
</div>
<div>
<h3>我自托管的服务</h3>
<ul>
<li>Authentik SSO、Mailcow、AList、Mem0</li>
<li>Umami 统计、Tor onion service</li>
<li>本地 GPU 上的 Whisper / TTS 推理</li>
</ul>
</div>
</div>
<h2>联系方式</h2>
<p>
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
<span class="sep">·</span>
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
<span class="sep">·</span>
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
<span class="sep">·</span>
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
</p>
<h2>面向马来西亚中小企业 —— Hoelee Enterprise</h2>
<div class="sme-block">
<p>
<strong>网站设计与开发是我的主业</strong>——为马来西亚中小企业做快速、好维护的网站,
从设计一直到它底下的托管、DNS、备份与监控。<strong>邮箱托管是次要副业</strong>,并非主业。
</p>
<p>
因为基建是我自己运维的,你面对的是一个人而不是两方——不会出现「写代码的人看不到服务器、
托管商读不懂代码」的情况。
</p>
<p>
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
<span class="sep">·</span>
<a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a>
</p>
</div>
<h2>联系</h2>
<div class="contact-split">
<div>
<h3>招聘 / 工程岗位</h3>
<p><a href={`mailto:${SITE.email}`}>{SITE.email}</a></p>
<p><a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a> · <a href={SITE.github} target="_blank" rel="noopener">GitHub</a></p>
<p>远程或混合;随时可以聊技术。</p>
</div>
<div>
<h3>商业项目 / 网站</h3>
<p><a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a></p>
<p><a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a></p>
<p>马来西亚中小企业:网站设计开发、托管与维护。</p>
</div>
</div>
<AuthorCard lang="zh" bio="base 在马来西亚的全栈开发者兼 DevOps 工程师。接受远程/混合办公机会。" />
</BaseLayout>
+14 -1
View File
@@ -29,7 +29,20 @@ const featuredPosts = featured.length === 4 ? featured : posts.slice(0, 4);
altLocaleUrl={`${SITE.url}/`}
>
<section class="hero">
<h1>{SITE.author} —— 全栈开发者兼 DevOps 工程师</h1>
<div class="hero-id">
<picture>
<source srcset={SITE.photo.smallWebp} type="image/webp" />
<img
class="hero-photo"
src={SITE.photo.small}
alt={`${SITE.author}(${SITE.handle})`}
width="72"
height="72"
decoding="async"
/>
</picture>
<h1>{SITE.author} —— 全栈开发者兼 DevOps 工程师</h1>
</div>
<p class="hero-sub">
我独自运维 <strong>三台自管服务器上的 162 个容器</strong>(客户站点、邮件平台、
CI/CD、流程自动化与这个博客),并为马来西亚中小企业构建 Web 系统。这里写的是
+93 -10
View File
@@ -284,20 +284,88 @@ pre code {
border-radius: var(--radius);
}
.author-card .avatar {
width: 56px;
height: 56px;
width: 64px;
height: 64px;
border-radius: 50%;
background: var(--brand);
color: #fff;
display: grid;
place-items: center;
font-weight: 700;
font-size: 1.3rem;
object-fit: cover;
border: 1px solid var(--border);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 25%, transparent);
flex-shrink: 0;
display: block;
}
.author-card .author-body { min-width: 0; }
.author-card .who { font-weight: 600; margin: 0; }
.author-card .bio { color: var(--muted); font-size: 0.9rem; margin: 0.25rem 0 0.5rem; }
.author-card .links { display: flex; gap: 0.75rem; font-size: 0.85rem; }
.author-card .links { display: flex; gap: 0.75rem; font-size: 0.85rem; flex-wrap: wrap; }
.author-card .author-split { font-size: 0.82rem; color: var(--muted); margin: 0.6rem 0 0; }
.author-card .author-split a { color: var(--muted); }
.author-card .author-split a:hover { color: var(--brand); }
/* ---------- About ---------- */
.about-hero {
display: flex;
gap: 1.75rem;
align-items: flex-start;
margin: 0.5rem 0 2.25rem;
padding-bottom: 1.75rem;
border-bottom: 1px solid var(--border);
}
.about-photo {
width: 190px;
height: 190px;
border-radius: var(--radius);
border: 1px solid var(--border);
/* The headshot's own dark-blue background sits close to the page background, so a
faint brand ring + shadow is what actually separates it from the UI. */
box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent),
0 10px 28px rgba(0, 0, 0, 0.35);
object-fit: cover;
flex-shrink: 0;
display: block;
}
.about-hero h1 { margin-bottom: 0.6rem; }
.about-hero h1 .handle { color: var(--muted); font-weight: 500; }
.about-lede { font-size: 1.02rem; color: var(--muted); margin-bottom: 0.75rem; }
.about-links { font-size: 0.9rem; margin-bottom: 0.5rem; }
.about-open { font-size: 0.9rem; color: var(--muted); margin: 0; }
.proof-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.85rem;
margin: 1rem 0 1.25rem;
}
.proof-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 0.85rem 0.9rem;
}
.proof-card .n { display: block; font-size: 1.45rem; font-weight: 700; line-height: 1.15; }
.proof-card .l { display: block; font-size: 0.78rem; color: var(--muted); margin-top: 0.25rem; }
.stack-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin: 1rem 0 1.5rem;
}
.stack-grid h3 { font-size: 0.95rem; margin: 0 0 0.4rem; }
.stack-grid ul { margin: 0; padding-left: 1.1rem; }
.stack-grid li { font-size: 0.88rem; color: var(--muted); margin: 0.15rem 0; }
.sme-block {
background: var(--surface);
border: 1px solid var(--border);
border-left: 3px solid var(--brand);
border-radius: var(--radius);
padding: 1rem 1.1rem;
margin: 1rem 0 1.5rem;
}
.sme-block p { margin: 0.4rem 0; }
.contact-split { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; margin-top: 1rem; }
.contact-split h3 { font-size: 0.95rem; margin: 0 0 0.35rem; }
.contact-split p { margin: 0.2rem 0; font-size: 0.9rem; color: var(--muted); }
/* ---------- Related posts ---------- */
.related { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border); }
@@ -346,7 +414,18 @@ pre code {
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border);
}
.hero h1 { margin-bottom: 0.75rem; }
.hero-id { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.75rem; }
.hero-photo {
width: 72px;
height: 72px;
border-radius: 50%;
border: 1px solid var(--border);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent);
object-fit: cover;
flex-shrink: 0;
display: block;
}
.hero h1 { margin: 0; }
.hero-sub { font-size: 1.05rem; color: var(--muted); max-width: 62ch; }
.hero-cta {
display: flex;
@@ -642,6 +721,10 @@ a.category-card:hover { border-color: var(--brand); }
h2 { font-size: 1.25rem; }
.related-grid { grid-template-columns: 1fr; }
.featured-grid { grid-template-columns: 1fr; }
.proof-grid { grid-template-columns: repeat(2, 1fr); }
.stack-grid, .contact-split { grid-template-columns: 1fr; }
.about-hero { flex-direction: column; gap: 1rem; }
.about-photo { width: 140px; height: 140px; }
.search-hint, .search-key { display: none; }
.author-card { flex-direction: column; align-items: flex-start; }
}