0551e8dd6e
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>
97 lines
4.6 KiB
HTML
97 lines
4.6 KiB
HTML
<!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>
|