1<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>What’s New â Replicants.ai</title><link rel="icon" type="image/png" href="/replicants-icon.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,wght@0,600;0,700;1,600&display=swap" rel="stylesheet"> 2<!-- â ï¸ BEFORE the inline <style> BELOW, DELIBERATELY, so this page keeps winning 3 every collision. Loaded so the top bar's Sign in and the footer are THE home 4 page's, not a second copy of them â Alex, 2026-08-25: *"Login should be 5 following the design on the home page, so it's the Sign In button that we got 6 on the home page⦠fix the footer so the footer matches the same footer as the 7 home page."* 8 9 Safe to load over a page with its own design system because marketing-site.css 10 is almost entirely class-scoped: its only unscoped element rules are 11 `html,body{background}` and `body{margin:0}`, and this page sets its own `body` 12 further down. Its `:root` vars are likewise overridden by the `:root` below for 13 every name the two share. Checked before adding, not assumed. 14 15 â ï¸ `.rhome` IS THE SCOPE THE FOOTER NEEDS. marketing-site.css styles the footer 16 as `.rhome .ends`, which is why the footer at the bottom of this page is wrapped 17 in a `.rhome` div â the same trick free-coaching-community.html already uses. --> 18<link rel="stylesheet" href="/css/marketing-site.css?v=20260902film"> 19<style> 20 :root { 21 --ground: #f5f0e4; /* warm cream */ 22 --ground-grad-a: #f7f2e8; 23 --ground-grad-b: #f1ebdb; 24 --band: #efe9f4; /* soft lavender-cream */ 25 --card: #ffffff; 26 --ink: #1b1726; /* near-black, faint purple bias */ 27 --ink-2: #6b6456; /* warm grey */ 28 --ink-3: #9a9184; /* warm light grey */ 29 --line: #e6dcc7; /* cream hairline */ 30 --line-2: #efe7d6; 31 --purple: #6d4aff; 32 --purple-ink: #5a34ec; /* link/text contrast on cream */ 33 --purple-wash: #ece6fb; 34 --purple-line: #ddd2f7; 35 --amber: #a06e1c; /* eyebrow labels */ 36 --dark: #1d1a2b; /* header pill + footer */ 37 --dark-ink: #b6afc4; 38 --shadow: 0 1px 2px rgba(29,26,43,.05), 0 10px 30px rgba(29,26,43,.07); 39 --shadow-hover: 0 2px 6px rgba(29,26,43,.07), 0 20px 46px rgba(109,74,255,.16); 40 --glow: 0 12px 34px rgba(109,74,255,.42); 41 --maxw: 1080px; 42 --serif: "Fraunces", Georgia, "Iowan Old Style", "Times New Roman", serif; 43 --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, system-ui, sans-serif; 44 45 /* ââ The tokens the borrowed home-page rules read ââââââââââââââââââââââ 46 â ï¸ marketing-site.css HAS NO DARK MODE â its `:root` is light-only 47 (`--paper:#ffffff`, `--ink2:#4d5168`, `--ink3:#8a8fa6`, `--accent:#6a4cf1`), 48 with no `prefers-color-scheme` block anywhere in the file. This page DOES 49 have dark mode. Left alone, the Sign in pill it now borrows would paint a 50 white background with mid-grey text onto a near-black header, and the 51 footer links would be light-mode grey on dark â the classic 52 one-theme's-text-on-the-other-theme's-ground fault. 53 54 Aliased to THIS page's own tokens rather than restated as hex, so they 55 follow every theme block below automatically: custom properties resolve at 56 use time, so `--paper: var(--card)` tracks `--card` through the media query, 57 `[data-theme="dark"]` and `[data-theme="light"]` without those blocks 58 needing to know these names exist. One line each, and no fourth place to 59 keep in step. 60 61 Only the four names the borrowed rules actually use are defined. `--line` 62 is deliberately NOT among them â this page already defines it, and its 63 cream hairline is the right one here. */ 64 --paper: var(--card); 65 --ink2: var(--ink-2); 66 --ink3: var(--ink-3); 67 --accent: var(--purple-ink); 68 } 69 @media (prefers-color-scheme: dark) { 70 :root {
71 --ground: #16131f; --ground-grad-a: #18141f; --ground-grad-b: #120f19; 72 --band: #1c1730; --card: #201b2c; 73 --ink: #f3eee2; --ink-2: #a89f92; --ink-3: #766e63; 74 --line: #322c3f; --line-2: #262133; 75 --purple: #8f74ff; --purple-ink: #a892ff; --purple-wash: #241d3b; --purple-line: #332b50; 76 --amber: #cf9a3e; --dark: #0e0b16; --dark-ink: #9a92a8; 77 --shadow: 0 1px 2px rgba(0,0,0,.5), 0 12px 32px rgba(0,0,0,.4); 78 --shadow-hover: 0 2px 6px rgba(0,0,0,.55), 0 22px 50px rgba(0,0,0,.55); 79 --glow: 0 12px 34px rgba(143,116,255,.4); 80 } 81 } 82 :root[data-theme="dark"] { 83 --ground: #16131f; --ground-grad-a: #18141f; --ground-grad-b: #120f19; 84 --band: #1c1730; --card: #201b2c; 85 --ink: #f3eee2; --ink-2: #a89f92; --ink-3: #766e63; 86 --line: #322c3f; --line-2: #262133; 87 --purple: #8f74ff; --purple-ink: #a892ff; --purple-wash: #241d3b; --purple-line: #332b50; 88 --amber: #cf9a3e; --dark: #0e0b16; --dark-ink: #9a92a8; 89 --shadow: 0 1px 2px rgba(0,0,0,.5), 0 12px 32px rgba(0,0,0,.4); 90 --shadow-hover: 0 2px 6px rgba(0,0,0,.55), 0 22px 50px rgba(0,0,0,.55); 91 --glow: 0 12px 34px rgba(143,116,255,.4); 92 } 93 :root[data-theme="light"] { 94 --ground: #f5f0e4; --ground-grad-a: #f7f2e8; --ground-grad-b: #f1ebdb; 95 --band: #efe9f4; --card: #ffffff; 96 --ink: #1b1726; --ink-2: #6b6456; --ink-3: #9a9184; 97 --line: #e6dcc7; --line-2: #efe7d6; 98 --purple: #6d4aff; --purple-ink: #5a34ec; --purple-wash: #ece6fb; --purple-line: #ddd2f7; 99 --amber: #a06e1c; --dark: #1d1a2b; --dark-ink: #b6afc4; 100 --shadow: 0 1px 2px rgba(29,26,43,.05), 0 10px 30px rgba(29,26,43,.07); 101 --shadow-hover: 0 2px 6px rgba(29,26,43,.07), 0 20px 46px rgba(109,74,255,.16); 102 --glow: 0 12px 34px rgba(109,74,255,.42); 103 } 104 105 * { box-sizing: border-box; } 106 body { 107 margin: 0; color: var(--ink); font-family: var(--sans); line-height: 1.6; 108 background: linear-gradient(180deg, var(--ground-grad-a), var(--ground-grad-b) 40%, var(--ground)); 109 -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; 110 } 111 .wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; } 112 a { color: inherit; text-decoration: none; } 113 114 /* Header */ 115 header.site { 116 position: sticky; top: 0; z-index: 50; 117 background: color-mix(in srgb, var(--ground) 84%, transparent); 118 backdrop-filter: saturate(1.3) blur(10px); 119 border-bottom: 1px solid var(--line); 120 } 121 .nav { display: flex; align-items: center; justify-content: space-between; height: 64px; } 122 .brand { display: flex; align-items: center; gap: 11px; } 123 .brand .mark { width: 30px; height: 30px; border-radius: 8px; background: radial-gradient(circle at 30% 25%, #fff, var(--purple-wash)); border: 1px solid var(--purple-line); display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(109,74,255,.18); } 124 .brand .mark b { font-family: var(--serif); font-weight: 700; font-size: 16px; color: var(--purple-ink); line-height: 1; } 125 .brand .word { font-weight: 700; letter-spacing: .16em; font-size: 15px; } 126 .nav-right { display: flex; align-items: center; gap: 14px; } 127 .nav-link { color: var(--ink-2); font-size: 14px; font-weight: 500; white-space: nowrap; } 128 .nav-link[aria-current] { color: var(--ink); } 129 .btn { font: inherit; font-size: 14px; font-weight: 600; border-radius: 999px; padding: 10px 20px; cursor: pointer; border: 1px solid transparent; transition: transform .15s ease, box-shadow .15s ease, background .15s ease; white-space: nowrap; display: inline-flex; align-items: center; gap: 8px; } 130 .btn-dark { color: #fff; background: var(--dark); } 131 .btn-dark:hover { transform: translateY(-1px); } 132 .btn-purple { color: #fff; background: linear-gradient(135deg, #7c5cff, #6134f0); box-shadow: var(--glow); } 133 .btn-purple:hover { transform: translateY(-1px); } 134 @media (max-width: 560px){ .hide-sm { display: none; } .nav-right { gap: 10px; } } 135 /* â ï¸ `.brand span`, NOT `.brand .word`. The wordmark used to be 136 `<span class="word">`; swapping in the home page's brand block dropped that 137 class, so this rule silently stopped matching and the nav overflowed the 138 viewport at 390px with "Get updates" running off the right edge. A media 139 query that no longer selects anything fails exactly this quietly. */ 140 @media (max-width: 440px){ .brand span { display: none; } } 141 142 /* Eyebrow */ 143 .eyebrow { text-transform: uppercase; letter-spacing: .18em; font-size: 12px; font-weight: 700; color: var(--amber); display: inline-flex; align-items: center; gap: 9px; } 144 .eyebrow .pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--purple); box-shadow: 0 0 0 0 var(--purple);
144 animation: pulse 2.6s infinite; } 145 @keyframes pulse { 0%{ box-shadow: 0 0 0 0 color-mix(in srgb, var(--purple) 55%, transparent);} 70%{ box-shadow: 0 0 0 9px transparent;} 100%{ box-shadow: 0 0 0 0 transparent;} } 146 147 /* Hero */ 148 .hero { padding: 66px 0 44px; } 149 h1.title { font-family: var(--serif); font-weight: 700; font-size: clamp(36px, 6.4vw, 60px); line-height: 1.06; letter-spacing: -.01em; margin: 20px 0 0; max-width: 16ch; text-wrap: balance; } 150 h1.title em { font-style: italic; color: var(--purple-ink); } 151 .lede { font-size: clamp(16px, 2.2vw, 19px); color: var(--ink-2); max-width: 54ch; margin: 20px 0 0; } 152 .stats { display: flex; flex-wrap: wrap; gap: 30px; margin-top: 32px; } 153 .stat .n { font-family: var(--serif); font-size: 30px; font-weight: 700; letter-spacing: -.01em; font-variant-numeric: tabular-nums; } 154 .stat .n .accent { color: var(--purple-ink); } 155 .stat .l { font-size: 13px; color: var(--ink-3); margin-top: 3px; } 156 157 section { padding: 46px 0; } 158 .section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 24px; } 159 .section-head .eyebrow { font-size: 12px; } 160 .date { font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; } 161 162 /* 72h band */ 163 .band { background: var(--band); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } 164 .fresh-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } 165 @media (max-width: 820px){ .fresh-grid { grid-template-columns: 1fr; } } 166 .fresh { background: var(--card); border: 1px solid var(--line); border-radius: 16px; padding: 22px; box-shadow: var(--shadow); position: relative; transition: transform .18s ease, box-shadow .18s ease; } 167 .fresh:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); } 168 .fresh .new { position: absolute; top: 16px; right: 16px; font-size: 11px; font-weight: 700; letter-spacing: .04em; color: #fff; background: linear-gradient(135deg,#7c5cff,#6134f0); padding: 3px 10px; border-radius: 999px; } 169 .fresh h3 { font-family: var(--serif); font-weight: 700; font-size: 19px; margin: 28px 0 8px; line-height: 1.24; letter-spacing: -.01em; } 170 .fresh p { font-size: 14px; color: var(--ink-2); margin: 0; } 171 .icon { width: 36px; height: 36px; border-radius: 10px; background: var(--purple-wash); color: var(--purple-ink); display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--purple-line); } 172 .icon svg { width: 18px; height: 18px; } 173 174 /* Feed timeline */ 175 .bucket { margin-bottom: 10px; } 176 .bucket-label { position: sticky; top: 64px; z-index: 5; background: color-mix(in srgb, var(--ground) 88%, transparent); backdrop-filter: blur(6px); padding: 12px 0; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .14em; color: var(--amber); } 177 .rail { position: relative; padding-left: 32px; } 178 .rail::before { content: ""; position: absolute; left: 7px; top: 8px; bottom: 8px; width: 2px; background: linear-gradient(var(--purple-line), transparent); } 179 .entry { position: relative; padding: 16px 0 26px; } 180 .entry::before { content: ""; position: absolute; left: -32px; top: 26px; width: 16px; height: 16px; border-radius: 50%; background: var(--card); border: 2px solid var(--purple); box-shadow: 0 0 0 4px var(--ground); } 181 .card { background: var(--card); border: 1px solid var(--line); border-radius: 18px; box-shadow: var(--shadow); overflow: hidden; display: grid; grid-template-columns: 1.3fr 1fr; transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; } 182 .card:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); border-color: var(--purple-line); } 183 @media (max-width: 720px){ .card { grid-template-columns: 1fr; } } 184 .card-body { padding: 24px 26px; } 185 .chips { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } 186 .chip { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: 5px 11px; border-radius: 999px; background: var(--card); color: var(--ink-2); border: 1px solid var(--line); box-shadow: 0 1px 2px rgba(29,26,43,.04); } 187 .chip.hl { color: var(--purple-ink); background: var(--purple-wash); border-color: var(--purple-line); } 188 .chip.beta { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); background: color-mix(in srgb, var(--amber) 10%, var(--card)); } 189 .card h3 { font-family: var(--serif); font-weight: 700; font-size: 22px; letter-spacing: -.01em; margin: 14px 0 9px; line-height: 1.2; } 190 .card p { color: var(--ink-2); font-size: 15px; margin: 0 0 18px; } 191 .see { display: inline-flex; align-items: center; gap: 7px; font-weight: 600; font-size: 14px; color: var(--purple-ink); } 192 .see .arw { transition: transform .18s ease; } 193 .card:hover .see .arw, .fresh:hover .see .arw { transform: translateX(4px); } 194 195 .shot { position: relative; background: linear-gradient(160deg, var(--band), color-mix(in srgb, var(--purple-wash) 55%, var(--band))); border-left: 1px solid var(--line-2); display: flex; align-items: center; justify-content: center; min-height: 200px; } 196 @media (max-width: 720px){ .shot { border-left: none; border-top: 1px solid var(--line-2); min-height: 160px; } } 197 .shot .frame { width: 78%; height: 70%; border-radius: 11px; background: var(--card); border: 1px solid var(--line); box-shadow: var(--shadow); display: flex; flex-direction: column; overflow: hidden; } 198 .shot .bar { height: 22px; background: var(--band); border-bottom: 1px solid var(--line-2); display: flex; align-items: center; gap: 5px; padding: 0 10px; } 199 .shot .bar i { width: 7px; height: 7px; border-radius: 50%; background: var(--line); display: inline-block; } 200 .shot .ph { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 9px; color: var(--ink-3); } 201 .shot .ph svg { width: 26px;
201 height: 26px; opacity: .75; } 202 .shot .ph span { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; } 203 204 /* Closing CTA (mirrors homepage) */ 205 .cta-band { text-align: center; } 206 .cta-inner { background: linear-gradient(180deg, var(--band), var(--ground)); border: 1px solid var(--line); border-radius: 24px; padding: 48px 24px; box-shadow: var(--shadow); } 207 .cta-inner h2 { font-family: var(--serif); font-weight: 700; font-size: clamp(26px, 4.5vw, 40px); line-height: 1.1; letter-spacing: -.01em; margin: 0 0 6px; max-width: 20ch; margin-inline: auto; text-wrap: balance; } 208 .cta-inner h2 em { font-style: italic; color: var(--purple-ink); } 209 .cta-inner .big { margin-top: 26px; font-size: 16px; padding: 16px 34px; } 210 .cta-sub { margin-top: 16px; font-size: 14px; color: var(--ink-3); } 211 .cta-sub b { color: var(--ink-2); font-weight: 600; } 212 213 /* ââ Borrowed-from-home bits, and the two overrides they need ââââââââââ 214 â ï¸ `.rtop-in` IS `position:absolute` IN marketing-site.css. It is written for 215 the home page's `.rtop`, which is `position:relative` and holds a centred 216 wordmark with the button pinned out to the right. Dropped into this page's 217 flex `.nav-right` unchanged it would position against whatever ancestor 218 happens to be positioned and land somewhere arbitrary â so it is put back in 219 flow here. The LOOK (the pill, the border, the hover) is inherited, which is 220 the part Alex asked to match. */ 221 .nav-right .rtop-in { position: static; transform: none; } 222 223 /* The home page's sub-headline under the pitch. Its own `.who` lives in the 224 `.rhome` scope, which this band is not inside, so it is restated rather than 225 the whole landing-page scope being pulled in around a changelog. */ 226 .cta-who { margin: 10px auto 0; max-width: 46ch; font-size: 16px; line-height: 1.5; color: var(--ink-2); } 227 .cta-trust { margin-top: 12px; display: flex; justify-content: center; gap: 8px 22px; flex-wrap: wrap; font-size: 13.5px; color: var(--ink-3); } 228 .cta-trust span::before { content: "\2022"; color: var(--purple); margin-right: 7px; } 229 /* â ï¸ TWO LINES, THE WAY HOME SETS IT â the sentence and the promise, not one 230 run-on paragraph. marketing-site.css does this with `.rhome h1 span,.rhome h1 231 em{display:block}`, and without the equivalent here the italic half started 232 mid-line and the whole thing read as prose. The home headline is also longer 233 than the one this band used to carry, so 20ch broke it into a column of 234 two-word lines. */ 235 .cta-inner h2 { max-width: 30ch; } 236 .cta-inner h2 span, .cta-inner h2 em { display: block; text-wrap: balance; } 237 238 /* Footer */ 239 footer.site { background: var(--dark); color: var(--dark-ink); margin-top: 8px; } 240 .foot { display: flex; flex-wrap: wrap; gap: 20px 40px; justify-content: space-between; align-items: center; padding: 36px 0; } 241 .foot .word { color: #fff; } 242 .foot .mark { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.16); } 243 .foot .mark b { color: #fff; } 244 .foot-links { display: flex; gap: 22px; flex-wrap: wrap; } 245 .foot-links a { color: var(--dark-ink); font-size: 14px; } 246 .foot-links a:hover { color: #fff; } 247 .foot .copy { width: 100%; border-top: 1px solid rgba(255,255,255,.09); padding-top: 20px; margin-top: 6px; font-size: 13px; color: color-mix(in srgb, var(--dark-ink) 78%, transparent); } 248 249 .reveal { opacity: 0; transform: translateY(14px); transition: opacity .6s ease, transform .6s ease; } 250 .reveal.in { opacity: 1; transform: none; } 251 @media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } .eyebrow .pulse { animation: none; } } 252 :focus-visible { outline: 2px solid var(--purple); outline-offset: 2px; border-radius: 4px; } 253 254 .note { max-width: var(--maxw); margin: 0 auto; padding: 14px 24px 0; } 255 .note .inner { font-size: 12.5px; color: var(--ink-3); background: color-mix(in srgb, var(--band) 60%, var(--ground)); border: 1px dashed var(--line); border-radius: 10px; padding: 10px 14px; } 256 257 /* Subscribe popover (Alex 2026-08-02 â status-page style "subscribe to 258 updates", restyled to this page's design). Anchored under the sticky 259 header on desktop; centred sheet on small screens. */ 260 .sub-pop { position: fixed; top: 74px; right: max(24px, calc((100vw - var(--maxw)) / 2 + 24px)); width: min(360px, calc(100vw - 32px)); background: var(--card); border: 1px solid var(--purple-line); border-radius: 18px; box-shadow: var(--shadow-hover); padding: 22px; z-index: 60; } 261 .sub-pop[hidden] { display: none; } 262 .sub-pop h3 { font-family: var(--serif); font-weight: 700; font-size: 20px; margin: 0 0 6px; letter-spacing: -.01em; } 263 .sub-pop .sub-copy { font-size: 13.5px; color: var(--ink-2); margin: 0 0 16px; line-height: 1.5; } 264 .sub-pop label { display: block; font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); margin: 12px 0 5px; } 265 .sub-pop input { width: 100%; font: inherit; font-size: 14.5px; color: var(--ink); background: color-mix(in srgb, var(--band) 40%, var(--card)); border: 1px solid var(--line); border-radius: 10px; padding: 10px 13px; outline: none; transition: border-color .15s ease, box-shadow .15s ease; } 266 .sub-pop input:focus { border-color: var(--purple); box-shadow: 0 0 0 3px color-mix(in srgb, var(--purple) 18%, transparent); } 267 .sub-pop input::placeholder { color: var(--ink-3); } 268 .sub-pop .sub-go { width: 100%; justify-content: center; margin-top: 16px; } 269 .sub-pop .sub-fine { font-size: 11.5px; color: var(--ink-3); margin: 12px 0 0; text-align: center; } 270 .sub-pop .sub-status { font-size: 13px; margin: 10px 0 0; display: none; } 271 .sub-pop .sub-status.err { display: block; color: #b3392f; } 272 .sub-pop .sub-done { display: none; text-align: center; padding: 10px 0 4px; } 273 .sub-pop.done .sub-done { display: block; } 274 .sub-pop.done .sub-form { display: none; } 275 .sub-pop .sub-done .tick { width: 44px; height: 44px; border-radius: 50%; background: var(--purple-wash); border: 1px solid var(--purple-line); color: var(--purple-ink); display: inline-flex; align-items: center; justify-content: center; margin-bottom: 10px; } 276 .sub-pop .sub-done .tick svg { width: 22px; height: 22px; } 277 .sub-pop .sub-done h3 { margin-bottom: 4px; } 278 .sub-pop .sub-done p { font-size: 13.5px; color: var(--ink-2); margin: 0; } 279 .sub-pop .sub-x { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border: none; border-radius: 8px; background: transparent; color: var(--ink-3); cursor: pointer; font-size: 16px; line-height: 1; } 280 .sub-pop .sub-x:hover { background: var(--band); color: var(--ink); } 281 .sub-scrim { position: fixed; inset: 0; z-index: 55; background: transparent; } 282 .sub-scrim[hidden] { display: none; } 283 @media (max-width: 560px) { 284 .sub-pop { position: fixed; top: auto; bottom: 16px; left: 16px; right: 16px; width: auto; } 285 .sub-scrim { background: rgba(29,26,43,.35); } 286 } 287</style> 288</head><body> 289 290<header class="site"> 291 <div class="wrap nav"> 292 <!-- â ï¸ THE REAL LOGO, NOT A CSS-DRAWN LETTER. This was a `<span class="mark">` 293 holding a styled "R" in a gradient square â a lookalike, not the mark. 294 Alex, 2026-08-25: *"On the What's New page we need to use a proper 295 replicants logo, the same one that we're using on the home page."* This is 296 now byte-for-byte the home page's brand block, so there is one logo to 297 change rather than two. --> 298 <a class="brand" href="/" aria-label="Replicants home"> 299 <img src="/replicants-icon.png" alt="">
300 <span>Replicants.ai</span> 301 </a> 302 <nav class="nav-right"> 303 <!-- â ï¸ "What's New" IS GONE FROM HERE ON PURPOSE â it linked to the page it 304 was already on. Alex: *"What's New doesn't need to be in the top menu."* 305 306 â ï¸ AND SO IS "Start free forever". *"Login can be in there but Start Free 307 Forever shouldn't be in there⦠Maybe 'Start your 14-day free trial' 308 should be there instead of the Free Forever thing."* Taken as: where a 309 paid CTA sits on this page, it is the trial â which is also what he was 310 unambiguous about for the closing band below. Free-forever is the 311 free-community offer and belongs on the offer page, not next to a 312 changelog. 313 314 â ï¸ Sign in is a LINK, not the home page's modal. `data-login` needs the 315 whole `#login-overlay` markup copied in as well, and a second copy of the 316 login form on a changelog page is exactly the duplication this round is 317 removing. `?login=1` is the same deliberate suffix home uses: bare 318 /app.html bounces anyone without a token straight back out, which is what 319 once made "Sign in" look dead. --> 320 <a class="rtop-in" href="/app.html?login=1">Sign in</a> 321 <button class="btn btn-purple" id="subOpen" type="button" aria-expanded="false" aria-controls="subPop"> 322 <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg> 323 Get updates 324 </button> 325 <a class="btn btn-dark hide-sm" href="/">Start your 14-day trial</a> 326 </nav> 327 </div> 328</header> 329 330<!-- Outside the header: its backdrop-filter makes it the containing block 331 for position:fixed, which pinned this to the header instead of the 332 viewport (clipped it on mobile). --> 333<div class="sub-scrim" id="subScrim" hidden></div> 334<div class="sub-pop" id="subPop" role="dialog" aria-modal="false" aria-labelledby="subTitle" hidden> 335 <button class="sub-x" id="subClose" type="button" aria-label="Close">×</button> 336 <form class="sub-form" id="subForm" novalidate> 337 <h3 id="subTitle">Get updates as they ship</h3> 338 <p class="sub-copy">New features land here first. Pop in your email, your mobile, or both, and we’
338ll let you know when something new goes live.</p> 339 <label for="subEmail">Email</label> 340 <input id="subEmail" name="email" type="email" autocomplete="email" placeholder="[email protected]" inputmode="email"> 341 <label for="subPhone">Mobile (for texts)</label> 342 <input id="subPhone" name="phone" type="tel" autocomplete="tel" placeholder="+44 7000 000000" inputmode="tel"> 343 <button class="btn btn-purple sub-go" id="subGo" type="submit">Subscribe</button> 344 <p class="sub-status" id="subStatus"></p> 345 <p class="sub-fine">No spam. Unsubscribe any time.</p> 346 </form> 347 <div class="sub-done"> 348 <span class="tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="m4.5 12.5 5 5 10-11"/></svg></span> 349 <h3>You’re on the list</h3> 350 <p>We’ll let you know when something new ships.</p> 351 </div> 352 </div> 353 354<main> 355 <section class="hero"> 356 <div class="wrap"> 357 <span class="eyebrow"><span class="pulse"></span>Product updates</span> 358 <h1 class="title">Your platform gets <em>better every day.</em></h1> 359 <p class="lede">We ship improvements to the websites, community and outreach tools almost every day — from small polish to whole new tools. Here’s what’s landed recently.</p> 360 <p style="margin:18px 0 0"><a class="see" href="https://start.replicants.ai/">𧪠Some features below are in beta — join the current beta <span class="arw">→</span></a></p> 361 <div class="stats" id="wnStats" hidden> 362 <div class="stat"><div class="n"><span class="accent" id="st72"></span></div><div class="l">shipped in the last 72 hours</div></div> 363 <div class="stat"><div class="n" id="stMonth"></div><div class="l">updates this month</div></div> 364 <div class="stat"><div class="n" id="stLast"></div><div class="l">last update</div></div> 365 </div> 366 </div> 367 </section> 368 369 <section class="band" id="freshBand" hidden> 370 <div class="wrap"> 371 <div class="section-head"><span class="eyebrow">Last 72 hours</span><span class="date" id="freshDate"></span></div> 372 <div class="fresh-grid" id="freshGrid"></div> 373 </div> 374 </section> 375 376 <section> 377 <div class="wrap"> 378 <div class="section-head"><span class="eyebrow">Everything shipped</span></div> 379 <div id="feed"><div class="date" id="wnEmpty">Loading updates…</div></div> 380 </div> 381 </section> 382 383 <section class="band" id="betaBand" hidden> 384 <div class="wrap"> 385 <div class="section-head"><span class="eyebrow" style="color:var(--amber)">𧪠In beta — not generally available yet</span><a class="see" href="https://start.replicants.ai/">Join the current beta <span class="arw">→</span></a></div> 386 <div id="betaFeed"></div> 387 </div> 388 </section> 389 390 <section class="cta-band"> 391 <div class="wrap"> 392 <!-- â ï¸ THIS IS THE HOME PAGE'S PITCH, WORD FOR WORD. Alex, 2026-08-25: 393 *"in the footer the sign-up message should basically be the same as the 394 home page: 'Describe the coaching business you want' message. Again it 395 should be the 14-day free trial that should go there."* 396 397 It replaced "Your business, always improving â without you lifting a 398 finger" + Start free forever: a second pitch and the wrong offer. Somebody 399 who has just read a changelog is being asked to buy the product, so they 400 should be asked in the words the product is actually sold in. 401 402 â ï¸ THE CTA IS A LINK TO THE HOME PAGE, NOT AN EMAIL BOX, and that is a 403 deliberate limit. home-signup.js would build the real box here â it only 404 needs a `.acts` host â but it opens with `gateOnSession`, which sends a 405 signed-in visitor straight to /app.html. On the home page that is the 406 point; on the changelog it would mean any logged-in coach following "See 407 what shipped this week" gets thrown into their dashboard instead of the 408 page they asked for. The box stays where the flow lives. 409 410 â ï¸ Copy kept in step with home.html by hand â if the hero headline 411 changes there, change it here. --> 412 <div class="cta-inner reveal in"> 413 <h2><span>Describe the coaching business you want.</span> <em>Watch it get built in front of you.</em></h2> 414 <p class="cta-who">Your website, your community, your courses, your funnels and your 415 emails. Six months of setting up, done in an afternoon.</p> 416 <a class="btn btn-purple big" href="/">Start your 14-day trial</a> 417 <div class="cta-trust"><span>No charge today</span><span>Cancel any time</span></div> 418 <div class="cta-sub"><b>New updates land here first.</b> <a class="see" href="#" id="subOpen2" style="font-size:14px">
418Get them by email or text <span class="arw">→</span></a></div> 419 </div> 420 </div> 421 </section> 422</main> 423 424<!-- â ï¸ THE HOME PAGE'S FOOTER, NOT A THIRD ONE. Alex, 2026-08-25: *"fix the footer 425 so the footer matches the same footer as the home page."* 426 427 What was here was a slim dark bar carrying the fake "R" mark, a self-referential 428 "What's New" link and "Log in" â three things this round is removing everywhere. 429 This is now the same markup as home.html's `<footer class="ends">`, styled by 430 the same `.rhome .ends` rules in marketing-site.css, which is why it is wrapped 431 in `.rhome` (`min-height:0` because that class is written for a full-height 432 landing page). free-coaching-community.html already does exactly this â the 433 pattern is established, not invented here. 434 435 â ï¸ "What's new" IS still in the footer, unlike the top bar, and points at this 436 page. That is the home page's link list reproduced faithfully; a footer that 437 silently differs from home's is the thing being fixed. --> 438<div class="rhome" style="min-height:0;padding-top:8px"> 439 <footer class="ends"> 440 <nav> 441 <a href="/whats-new">What’s new</a> 442 <a href="https://ridingthewave.coach" target="_blank" rel="noopener">Join our community</a> 443 <a href="/app.html?login=1">Sign in</a> 444 </nav> 445 <nav class="legal"> 446 <a href="/terms" target="_blank" rel="noopener">Terms</a> 447 <a href="/privacy" target="_blank" rel="noopener">Privacy</a> 448 </nav> 449 <p>Replicants.ai</p> 450 </footer> 451</div> 452
453<script src="/js/date-format.js?v=20261002ae2"></script>
vendor: 1 bytes, line 453
453
454<script> 455(function () { 456 function esc(s) { var d = document.createElement('div'); d.textContent = String(s == null ? '' : s); return d.innerHTML; } 457 function when(ts) { return App.Dates.short(ts, { year: 'auto' }); } 458 function ago(ts) { 459 var m = Math.round((Date.now() - new Date(ts).getTime()) / 60000); 460 if (m < 60) return m + 'm ago'; 461 if (m < 48 * 60) return Math.round(m / 60) + 'h ago'; 462 return Math.round(m / 1440) + 'd ago'; 463 } 464 function weekLabel(d) { 465 var day = d.getDay() || 7; var mon = new Date(d); mon.setDate(d.getDate() - day + 1); 466 var sun = new Date(mon); sun.setDate(mon.getDate() + 6); 467 var f = function (x) { return App.Dates.short(x, { year: false }); }; 468 return f(mon) + ' â ' + f(sun); 469 } 470 var shotPh = '<div class="shot" aria-hidden="true"><div class="frame"><div class="bar"><i></i><i></i><i></i></div>' 471 + '<div class="ph"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="3" y="3" width="18" height="18" rx="3"/><path d="m3 15 5-5 4 4 3-3 6 6"/><circle cx="8.5" cy="8.5" r="1.5"/></svg><span>Screenshot</span></div></div></div>'; 472 473 fetch('/api/whats-new?ts=' + Date.now(), { cache: 'no-store' }).then(function (r) { return r.json(); }).then(function (data) { 474 var all = (data && data.entries) || []; 475 var now = Date.now(); 476 var pub = function (e) { return new Date(e.published_at || e.shipped_at); }; 477 478 // Live first, beta in its own band underneath (Alex 2026-07-22): mixing 479 // them made the feed read as "everything is beta". Stats + fresh grid + 480 // the weekly timeline are LIVE-only; beta renders as one separate rail. 481 var entries = all.filter(function (e) { return e.stage !== 'beta'; }); 482 var betaEntries = all.filter(function (e) { return e.stage === 'beta'; }); 483 484 if (!all.length) { document.getElementById('wnEmpty').textContent = 'Updates are on their way â check back soon.'; return; } 485 486 var entryCard = function (e) { 487 // Two rendering modes. A real screenshot (raster) fills the panel 488 // edge-to-edge with object-fit: cover. An SVG mock renders in a box 489 // FIXED to the mock canvas's own 1200:760 shape, centred on the band â 490 // the .shot panel's height follows the entry's TEXT, so every entry's 491 // box was a different shape and identical margins were geometrically 492 // impossible from the image side (Alex, 1â2 Aug, three rounds of it). 493 // aspect-ratio pins the box; the whole canvas, margins and all, is 494 // always visible and every entry frames identically. 495 var isMock = /\.svg(\?|$)/i.test(e.screenshot_url || ''); 496 var shot = e.screenshot_url 497 ? (isMock 498 ? '<div class="shot"><img src="' + esc(e.screenshot_url) + '" alt="" style="width:100%;aspect-ratio:1200/760;object-fit:cover;display:block"></div>' 499 : '<div class="shot" style="background:none;padding:0"><img src="' + esc(e.screenshot_url) + '" alt="" style="width:100%;height:100%;object-fit:cover;display:block"></div>') 500 : shotPh; 501 return '<div class="entry reveal in"><article class="card"><div class="card-body">' 502 + '<div class="chips">' + (e.area ? '<span class="chip hl">
502' + esc(e.area) + '</span>' : '') + (e.stage === 'beta' ? '<span class="chip beta">𧪠In beta</span>' : '') + '<span class="date">' + when(pub(e)) + '</span></div>' 503 + '<h3>' + esc(e.title) + '</h3><p>' + esc(e.blurb) + '</p>' 504 + (e.cta_url ? '<a class="see" href="' + esc(e.cta_url) + '">Take a look <span class="arw">→</span></a>' : (e.stage === 'beta' ? '<a class="see" href="https://start.replicants.ai/">Join the current beta <span class="arw">→</span></a>' : '')) 505 + '</div>' + shot + '</article></div>'; 506 }; 507 508 if (betaEntries.length) { 509 document.getElementById('betaFeed').innerHTML = '<div class="bucket"><div class="rail">' + betaEntries.map(entryCard).join('') + '</div></div>'; 510 document.getElementById('betaBand').hidden = false; 511 } 512 if (!entries.length) { document.getElementById('wnEmpty').textContent = 'Updates are on their way â check back soon.'; return; } 513 514 var fresh = entries.filter(function (e) { return now - pub(e).getTime() < 72 * 3600 * 1000; }); 515 var monthCount = entries.filter(function (e) { var d = pub(e), n = new Date(); return d.getMonth() === n.getMonth() && d.getFullYear() === n.getFullYear(); }).length; 516 document.getElementById('st72').textContent = fresh.length; 517 document.getElementById('stMonth').textContent = monthCount; 518 document.getElementById('stLast').textContent = ago(pub(entries[0])); 519 document.getElementById('wnStats').hidden = false; 520 521 if (fresh.length) { 522 document.getElementById('freshDate').textContent = when(pub(fresh[0])); 523 document.getElementById('freshGrid').innerHTML = fresh.slice(0, 3).map(function (e) { 524 return '<article class="fresh reveal in"><span class="new">' + (e.stage === 'beta' ? 'Beta' : 'New') + '</span>' 525 + '<span class="icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 2 2 7l10 5 10-5-10-5Z"/><path d="m2 17 10 5 10-5M2 12l10 5 10-5"/></svg></span>' 526 + '<h3>' + esc(e.title) + '</h3><p>' + esc(e.blurb) + '</p></article>'; 527 }).join(''); 528 document.getElementById('freshBand').hidden = false; 529 } 530 531 var buckets = {}; var order = []; 532 entries.forEach(function (e) { 533 var l = weekLabel(pub(e)); 534 if (!buckets[l]) { buckets[l] = []; order.push(l); } 535 buckets[l].push(e); 536 }); 537 document.getElementById('feed').innerHTML = order.map(function (label) { 538 return '<div class="bucket"><div class="bucket-label">' + esc(label) + '</div><div class="rail">' 539 + buckets[label].map(entryCard).join('') + '</div></div>'; 540 }).join(''); 541 }).catch(function () { 542 document.getElementById('wnEmpty').textContent = 'Couldnât load updates right now.'; 543 }); 544})(); 545 546// ââ Subscribe popover ââ 547(function () { 548 var pop = document.getElementById('subPop'); 549 var scrim = document.getElementById('subScrim'); 550 var openBtn = document.getElementById('subOpen'); 551 var form = document.getElementById('subForm'); 552 var status = document.getElementById('subStatus'); 553 var go = document.getElementById('subGo'); 554 555 function open() { 556 pop.hidden = false; scrim.hidden = false; 557 openBtn.setAttribute('aria-expanded', 'true'); 558 var first = document.getElementById('subEmail'); 559 if (first && !pop.classList.contains('done')) first.focus(); 560 } 561 function close() { 562 pop.hidden = true; scrim.hidden = true; 563 openBtn.setAttribute('aria-expanded', 'false'); 564 } 565 openBtn.addEventListener('click', function () { pop.hidden ? open() : close(); }); 566 var open2 = document.getElementById('subOpen2'); 567 if (open2) open2.addEventListener('click', function (ev) { 568 ev.preventDefault(); 569 window.scrollTo({ top: 0, behavior: 'smooth' }); 570 open(); 571 }); 572 document.getElementById('subClose').addEventListener('click', close); 573 scrim.addEventListener('click', close); 574 document.addEventListener('keydown', function (ev) { if (ev.key === 'Escape' && !pop.hidden) close(); }); 575 576 function showErr(msg) { status.textContent = msg; status.className = 'sub-status err'; } 577 578 form.addEventListener('submit', function (ev) { 579 ev.preventDefault(); 580 var email = document.getElementById('subEmail').value.trim(); 581 var phone = document.getElementById('subPhone').value.trim(); 582 if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return showErr('That email doesnât look right.');
583 if (!email && phone.replace(/\D/g, '').length < 7) return showErr(phone ? 'That number doesnât look right.' : 'Add an email or a mobile number.'); 584 status.className = 'sub-status'; status.textContent = ''; 585 go.disabled = true; go.textContent = 'Subscribingâ¦'; 586 fetch('/api/whats-new/subscribe', { 587 method: 'POST', 588 headers: { 'Content-Type': 'application/json' }, 589 body: JSON.stringify({ email: email, phone: phone }) 590 }).then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); }) 591 .then(function (res) { 592 go.disabled = false; go.textContent = 'Subscribe'; 593 if (res.ok && res.d && res.d.ok) { pop.classList.add('done'); return; } 594 if (res.d && res.d.error === 'rate_limited') return showErr('Too many tries. Give it a minute and try again.'); 595 showErr('Couldnât subscribe just now. Please try again.'); 596 }) 597 .catch(function () { 598 go.disabled = false; go.textContent = 'Subscribe'; 599 showErr('Couldnât subscribe just now. Please try again.'); 600 }); 601 }); 602})(); 603</script>
603 604</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.