Files
pig/apps/web/index.html
T
karti 0551e8dd6e Add link previews and icons; make PIG_INVITE_CODE actually work
Social and icons. A 1200x630 card, apple-touch-icon, and maskable PWA icons,
generated from an HTML template by a script so the mark, wordmark and tagline
cannot drift from the product. The apple-touch-icon referenced in index.html
was a 404 until now. Icons are drawn on an opaque plate with inset because iOS
rounds corners and Android may apply a circle — an edge-to-edge mark loses its
ears to that crop. og:image is absolute, which is the single most common
reason a card unfurls blank.

PIG_INVITE_CODE was a lie. Signup validates against the invites table, so
setting the variable only flipped a label in the UI — an operator would set it,
hand the code to a colleague, and watch them be rejected. It is now reconciled
into a real invite row at boot: setting it issues, changing it rotates and
revokes the predecessor, and removing it revokes. Verified all three, plus that
a restart with an unchanged code does not duplicate.

Two bugs found while doing that:

- `uses_remaining` was jsonb, so the SQL decrement could never have worked.
  Now integer. The generated migration failed because Postgres has no implicit
  jsonb->integer cast, so the USING clause is hand-written.
- Redemption keyed off `redeemedAt`, which would have made every reusable
  invite single-use — a confusing way to lock a team out. Availability now
  comes from `usesRemaining`, and redemption records who used it most recently
  without consuming it.

Sign-in gains a password option alongside the magic link, defaulting to
password since that is the daily path. PIG stores neither; both are handled by
the identity provider and PIG only ever sees the resulting token.
autocomplete is set so password managers and iOS can fill.

Verified: full migration chain applies to a fresh Postgres, the CSP hash for
the inline theme script is unchanged by the rebuild.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 19:43:10 -07:00

97 lines
4.6 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<!--
viewport-fit=cover plus the safe-area padding in index.css is what makes
this sit correctly on notched iPhones. Without it the bottom navigation
hides under the home indicator.
Note the absence of maximum-scale / user-scalable=no: disabling zoom is a
real accessibility failure, and iOS ignores it in recent versions anyway.
The 16px minimum font size on inputs (see index.css) is the correct fix
for Safari's zoom-on-focus behaviour.
-->
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="description" content="PIG — Prime Intellect Growth. An agent-native CRM for two-sided AI-compute companies." />
<!-- Matches the app chrome so Safari's toolbar blends rather than banding. -->
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#09090b" media="(prefers-color-scheme: dark)" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="pig" />
<!--
Icons. The SVG is preferred by every modern browser and adapts to the
system theme; the PNG is the fallback for those that do not. The
apple-touch-icon is drawn on an opaque plate with inset, because iOS
rounds the corners itself and a transparent edge-to-edge mark loses its
ears to the crop.
-->
<link rel="icon" href="/pig.svg" type="image/svg+xml" />
<link rel="icon" href="/icon-192.png" type="image/png" sizes="192x192" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/manifest.webmanifest" />
<title>pig — Prime Intellect Growth</title>
<link rel="canonical" href="https://primeintellectgrowth.com/" />
<!--
Link previews. og:image must be an ABSOLUTE url — unfurlers fetch it
without a page context, so a relative path silently yields no image,
which is the single most common reason a card renders blank.
summary_large_image gives the 1200×630 card rather than a small square
thumbnail. Both og: and twitter: tags are present because Slack, Discord,
LinkedIn and iMessage read Open Graph while X prefers its own.
-->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="pig" />
<meta property="og:url" content="https://primeintellectgrowth.com/" />
<meta property="og:title" content="pig — Prime Intellect Growth" />
<meta
property="og:description"
content="An open-source, agent-native CRM for companies that buy GPU capacity and sell it. Margin, utilisation and idle capacity from one ledger."
/>
<meta property="og:image" content="https://primeintellectgrowth.com/og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="pig — Prime Intellect Growth. The CRM for companies that buy compute and sell it." />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="pig — Prime Intellect Growth" />
<meta
name="twitter:description"
content="An open-source, agent-native CRM for companies that buy GPU capacity and sell it. Margin, utilisation and idle capacity from one ledger."
/>
<meta name="twitter:image" content="https://primeintellectgrowth.com/og.png" />
<script>
// Applied before first paint so a dark-mode user never sees a white
// flash. Reads the last-known preference from localStorage; the
// authoritative value arrives from the server moments later and
// reconciles silently.
(function () {
try {
var mode = localStorage.getItem('pig.themeMode') || 'system';
var accent = localStorage.getItem('pig.accent') || 'pig';
var dark =
mode === 'dark' ||
(mode === 'system' &&
window.matchMedia('(prefers-color-scheme: dark)').matches);
var root = document.documentElement;
root.setAttribute('data-theme', dark ? 'dark' : 'light');
root.setAttribute('data-accent', accent);
} catch (e) {
/* Private browsing can throw on localStorage. Defaults are fine. */
}
})();
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>