Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@ export default defineConfig({
provider: fontProviders.google(),
name: "Doto",
cssVariable: "--font-main",
weights: [400, 700],
weights: [400, 500, 700],
styles: ["normal"],
subsets: ["latin"],
display: "swap",
},
{
provider: fontProviders.google(),
name: "JetBrains Mono",
cssVariable: "--font-body",
weights: [300, 400, 500],
styles: ["normal"],
subsets: ["latin"],
display: "swap",
Expand Down
390 changes: 390 additions & 0 deletions docs/plans/2026-06-02-experience-site-revamp-design.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions public/humans.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* TEAM */

the experience company
hackers · engineers · designers

/* WHAT WE'RE BUILDING */

gaia ............... personal AI assistant — live → heygaia.io
smart glasses ...... in development
household robotics . on the horizon

/* SAY HI */

hi@heygaia.io

the screen was just where we started.
37 changes: 37 additions & 0 deletions src/components/sections/Close.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
import Section from "../ui/Section.astro";
import { siteConfig } from "../../config/site";

const twitter = siteConfig.social.twitter?.replace("@", "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Don't render the X link from an optional handle.

siteConfig.social.twitter is treated as optional here, but the anchor always renders. If that config is missing, this ships https://twitter.com/undefined as a broken contact link.

Proposed fix
 const twitter = siteConfig.social.twitter?.replace("@", "");
---
-      <a
-        class="link-bracket"
-        href={`https://twitter.com/${twitter}`}
-        target="_blank"
-        rel="noopener"
-      >
-        x
-      </a>
+      {twitter && (
+        <a
+          class="link-bracket"
+          href={`https://twitter.com/${twitter}`}
+          target="_blank"
+          rel="noopener"
+        >
+          x
+        </a>
+      )}

Also applies to: 27-34

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/sections/Close.astro` at line 5, The X/Twitter anchor in
Close.astro is always rendered even when siteConfig.social.twitter is missing,
which produces a broken undefined link. Update the Close component so the
twitter handle is only normalized and rendered when the optional config value
exists, and otherwise omit the link block entirely; use the existing twitter
variable and the anchor section around the social links to gate the output.

---

<Section index="06">
<blockquote cite="https://en.wikipedia.org/wiki/Steve_Jobs">
"The people who are crazy enough to think they can change the world are the
ones who do."
<cite>— Steve Jobs</cite>
</blockquote>

<div class="contact">
<p class="prose">If you want to help build this, we'd love to meet you.</p>
<div class="links">
<a class="link-bracket" href={`mailto:${siteConfig.social.email}`}>email</a>
<a
class="link-bracket"
href={`https://github.com/${siteConfig.social.github}`}
target="_blank"
rel="noopener"
>
github
</a>
<a
class="link-bracket"
href={`https://twitter.com/${twitter}`}
target="_blank"
rel="noopener"
>
x
</a>
</div>
</div>
</Section>
20 changes: 20 additions & 0 deletions src/components/sections/Connection.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
import Section from "../ui/Section.astro";
import ImageFrame from "../ui/ImageFrame.astro";
---

<Section index="05" class="connect">
<div class="stack fullbleed">
<ImageFrame label="march of progress" size="wide" />
<ImageFrame label="the touch — creation of adam" size="wide" />
</div>

<p class="payoff">
People ask why a software company is building glasses and robots. We don't
really think of ourselves as a software company. We think most of what fills
your day — the digital admin, the errands, the housework — is work you never
should have been doing. GAIA takes the digital load. The glasses carry it
with you. Robots take the physical load at home.
<span class="last">The screen was just where we started.</span>
</p>
</Section>
38 changes: 38 additions & 0 deletions src/components/sections/Gaia.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
import Section from "../ui/Section.astro";
import ImageFrame from "../ui/ImageFrame.astro";
---

<Section index="03">
<div class="label">
<span class="name">gaia</span>
<span class="status live"><span class="live-dot"></span>live today</span>
</div>

<div class="row center">
<div class="cell media">
<ImageFrame label="wireframe planet" caption="fig. 02 — gaia" />
</div>
<div class="cell">
<p class="prose">
Most assistants wait for you to ask, then forget you the second you
close the tab. GAIA doesn't. It reads your inbox before you do, watches
your calendar, drafts your replies, and runs the multi-step work you'd
never get around to — then remembers all of it, so it's sharper next
month than it is today.
</p>
<p class="prose">
It's live, open source, and already doing this.
<a class="link-inline" href="https://heygaia.io" target="_blank" rel="noopener"
>→ heygaia.io</a
>
</p>
<p class="prose">
Where it's headed: off the screen and onto your body. Paired with the
glasses, GAIA goes everywhere you go — the goal is simple, and a little
unreasonable: an assistant that can handle anything you'd handle
yourself. Everyone deserves a Jarvis.
</p>
</div>
</div>
</Section>
10 changes: 10 additions & 0 deletions src/components/sections/Hero.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
import Section from "../ui/Section.astro";
---

<Section index="00" class="hero">
<h1>We build technology that makes people more capable.</h1>
<p class="sub">Technology should give you your life back.</p>
<p class="sub">We're building a better experience for being human.</p>
<div class="cue">↓ scroll</div>
</Section>
28 changes: 28 additions & 0 deletions src/components/sections/Manifesto.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
import Section from "../ui/Section.astro";
import ImageFrame from "../ui/ImageFrame.astro";
---

<Section index="01">
<div class="row center">
<div class="cell">
<p class="prose">
We're a small team of hackers, engineers, and designers. We build the
things we wish existed.
</p>
<p class="prose">
We think the best technology gives you more and asks for less — more
time, more attention, more room to be human — and then gets out of the
way. Most technology today does the opposite. We're building the other
kind.
</p>
<p class="prose">
Everything worth building looks impossible right up until someone builds
it. That's the part we like.
</p>
</div>
<div class="cell media">
<ImageFrame label="human → machine" caption="fig. 00 — the extension" />
</div>
</div>
</Section>
31 changes: 31 additions & 0 deletions src/components/sections/Robotics.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
import Section from "../ui/Section.astro";
import ImageFrame from "../ui/ImageFrame.astro";
---

<Section index="04">
<div class="label">
<span class="name">household robotics</span>
<span class="status">on the horizon</span>
</div>

<div class="row center">
<div class="cell">
<p class="lead">
AI learned to make art and music — the parts of being human we'd least
want to give up — while the laundry still piles up in the other room. We
think that's backwards. The boring, physical, repetitive work is exactly
what machines should take off our hands.
</p>
<p class="prose" style="margin-top:18px">
We haven't built anything yet; this is a conviction more than a product.
The home is the last place technology really forgot, and the chores that
quietly eat your evenings are the ones worth automating first. It's the
hardest thing on this page — and the one we're most sure about.
</p>
</div>
<div class="cell media">
<ImageFrame label="home robot blueprint" caption="fig. 03 — household unit" />
</div>
</div>
</Section>
60 changes: 60 additions & 0 deletions src/components/sections/SmartGlasses.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
import Section from "../ui/Section.astro";
import ImageFrame from "../ui/ImageFrame.astro";
import Redacted from "../ui/Redacted.astro";

const specs = [
{ k: "display", v: "visible only to you" },
{ k: "voice", v: '"hey gaia"' },
{ k: "navigation", v: "painted on the road, live" },
{ k: "memory", v: "auto-captures what matters" },
{ k: "control", v: "voice · eyes · blink · hands" },
{ k: "audio", v: "bone conduction" },
{ k: "lenses", v: "clear → sunglasses, one press" },
{ k: "power", v: "solar across the frame" },
{ k: "build", v: "light, affordable, not ugly" },
];
---

<Section index="02">
<div class="label">
<span class="name">smart glasses</span>
<span class="status">in development</span>
</div>

<ImageFrame
label="smart glasses blueprint"
caption="fig. 01 — front elevation"
size="wide"
/>

<p class="lead" style="margin-top:34px">
GAIA, on your face. A display only you can see, a camera that sees what you
see, and an assistant you just talk to. It paints your directions onto the
road ahead, picks up the call you don't want to take, and remembers the
things you'd have forgotten by dinner. You run it with your voice, your
eyes, and your hands — no controller, no phone in your palm.
</p>
<p class="prose" style="margin-top:18px">
Most smart glasses are bricks you'd never wear in public. These are meant to
be light, cheap, and good-looking enough that you forget they're smart at
all.
</p>

<div class="specs">
{
specs.map((s) => (
<div class="spec">
<span class="k">{s.k}</span>
<span class="dots" />
<span class="v">{s.v}</span>
</div>
))
}
<div class="spec">
<span class="k">field of view</span>
<span class="dots"></span>
<span class="v"><Redacted text="classified" /></span>
</div>
</div>
</Section>
35 changes: 35 additions & 0 deletions src/components/ui/ImageFrame.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
interface Props {
/** drop in a real image path (e.g. /glasses.webp) to replace the placeholder */
src?: string;
alt?: string;
/** placeholder label shown until a real image exists */
label: string;
/** small blueprint caption under the frame, e.g. "fig. 01 — smart glasses" */
caption?: string;
size?: "wide" | "tall";
class?: string;
}

const {
src,
alt = "",
label,
caption,
size = "tall",
Comment on lines +16 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve an accessible name when src is used.

Line 16 defaults alt to "", and the image branch on Line 27 drops the required label. The first real asset added through this component will render as decorative unless every caller remembers to supply a separate alt.

Suggested fix
 const {
   src,
-  alt = "",
+  alt,
   label,
   caption,
   size = "tall",
   class: cls = "",
 } = Astro.props as Props;
@@
-        <img src={src} alt={alt} loading="lazy" decoding="async" />
+        <img src={src} alt={alt ?? label} loading="lazy" decoding="async" />

Also applies to: 27-28

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/ImageFrame.astro` around lines 16 - 19, The ImageFrame
component is defaulting alt to an empty string and the image render path is not
preserving an accessible name when src is used. Update ImageFrame.astro so the
Image branch uses the provided label as the default accessible text (or
otherwise requires a meaningful alt/label pairing) instead of silently rendering
decorative images. Keep the fix localized to the ImageFrame props/defaults and
the image rendering logic so callers don’t have to remember to supply both
fields.

class: cls = "",
} = Astro.props as Props;
---

<figure class:list={["frame-fig", cls]}>
<div class:list={["box", size]}>
{
src ? (
<img src={src} alt={alt} loading="lazy" decoding="async" />
) : (
<span>[ {label} ]</span>
)
}
</div>
{caption && <figcaption>{caption}</figcaption>}
</figure>
11 changes: 11 additions & 0 deletions src/components/ui/Redacted.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
interface Props {
/** text to hide behind the bar; also the screen-reader value */
children?: never;
text?: string;
}

const { text = "classified" } = Astro.props as Props;
---

<span class="redacted" title="classified">{text}</span>
14 changes: 14 additions & 0 deletions src/components/ui/Section.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
interface Props {
index?: string;
id?: string;
class?: string;
}

const { index, id, class: cls = "" } = Astro.props as Props;
---

<section id={id} class:list={["section", cls]}>
{index && <span class="sec-index" aria-hidden="true">{index}</span>}
<slot />
</section>
64 changes: 10 additions & 54 deletions src/layouts/Footer.astro
Original file line number Diff line number Diff line change
@@ -1,60 +1,16 @@
---
import Evolution from "../components/Evolution.astro";
import { siteConfig } from "../config/site";

const currentYear = new Date().getFullYear();

const socialLinks = [
{
name: "Twitter",
url: siteConfig.social.twitter
? `https://twitter.com/${siteConfig.social.twitter.replace("@", "")}`
: null,
enabled: !!siteConfig.social.twitter,
external: true,
},
{
name: "GitHub",
url: siteConfig.social.github
? `https://github.com/${siteConfig.social.github}`
: null,
enabled: !!siteConfig.social.github,
external: true,
},
{
name: "Email",
url: siteConfig.social.email ? `mailto:${siteConfig.social.email}` : null,
enabled: !!siteConfig.social.email,
external: false,
},
].filter((link) => link.enabled);
const version = "v0.1.0";
---

<footer class="text-sm space-y-2 relative" role="contentinfo">
<nav
class="lowercase flex flex-wrap gap-3 text-zinc-300"
aria-label="Social links"
>
{
socialLinks.map((link) => (
<a
href={link.url}
target={link.external ? "_blank" : undefined}
rel={link.external ? "noopener noreferrer" : undefined}
class="hover:outline hover:outline-zinc-500 hover:outline-dashed bg-black flex justify-center w-fit p-1 underline hover:no-underline"
>
{link.name}
</a>
))
}
</nav>

<slot />

<p class="text-zinc-400">
Copyright © {currentYear}
{siteConfig.name}. All rights reserved.
</p>
<footer class="footer" role="contentinfo">
<div class="braille" aria-hidden="true">
⠀⠀⢀⣠⣴⣶⣿⣿⣷⣶⣦⣄⡀⠀⠀⠀⠀⢀⣠⣶⣿⣿⣿⣶⣄⡀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣷⣦⣀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣷⣦⣄⡀⠀⠀⠀⣠⣶⣿⣿⣿⣿⣿⣷⣦⡀
⠀⢠⣾⣿⣿⠿⠛⠛⠿⣿⣿⣷⡄⠀⠀⢠⣾⣿⡿⠟⠛⠿⣿⣷⡄⠀⠀⢠⣾⣿⡿⠛⠛⠿⣿⣷⡄⠀⢠⣾⣿⡿⠛⠛⢿⣿⣷⡄⠀⢀⣾⣿⡿⠛⠛⢿⣿⣷⡀
</div>

<!-- <img src="/evolution.webp" class="sm:w-[35%] py-4 w-full" alt="Evolution" /> -->
<div class="foot-row">
<span class="version">{version} · the experience company</span>
<span class="copyright">© {currentYear} all rights reserved.</span>
</div>
</footer>
Loading