| new file mode 100644 |
| index 000000000..ddab558c8 |
| --- /dev/null |
| +++ b/implementation/carloku-paid-launch/copy-review/billing-page/preview/index.html |
| @@ -0,0 +1,1123 @@ |
| +<!doctype html> |
| +<html lang="en"> |
| +<head> |
| +<meta charset="utf-8"> |
| +<meta name="viewport" content="width=device-width, initial-scale=1"> |
| +<title>Billing · CARLOS console</title> |
| +<style> |
| + :root { |
| + --bg: #ffffff; --ink: #201d29; --muted: #6f6480; |
| + --accent: #5b3e96; --accent-strong: #4c3380; |
| + --accent-soft: rgba(91, 62, 150, 0.09); |
| + --rule: #eae7f0; --bad: #b3403a; --warn: #a6790f; --good: #2f7a4f; |
| + --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
| + --sans: ui-sans-serif, system-ui, -apple-system, sans-serif; |
| + } |
| + @media (prefers-color-scheme: dark) { |
| + :root { |
| + --bg: #16141c; --ink: #f0eef6; --muted: #a99ebd; |
| + --accent: #b394f2; --accent-strong: #c9b2f7; |
| + --accent-soft: rgba(179, 148, 242, 0.12); |
| + --rule: #2c2836; --bad: #e07b76; --warn: #d9a94a; --good: #5fae7f; |
| + } |
| + } |
| + [hidden] { display: none !important; } |
| + * { margin: 0; padding: 0; box-sizing: border-box; } |
| + body { |
| + background: var(--bg); color: var(--ink); |
| + font: 400 1rem/1.6 var(--sans); |
| + -webkit-font-smoothing: antialiased; padding: 0 1.25rem; |
| + } |
| + a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; } |
| + code { |
| + font-family: var(--mono); |
| + font-size: 0.88em; background: var(--accent-soft); |
| + padding: 0.1em 0.35em; border-radius: 0.25rem; |
| + } |
| + pre { |
| + background: var(--accent-soft); border-radius: 0.5rem; |
| + padding: 0.9rem 1.1rem; margin: 0 0 1rem; max-width: 40rem; |
| + overflow-x: auto; font-family: var(--mono); font-size: 0.85rem; line-height: 1.6; |
| + } |
| + pre code { background: none; padding: 0; } |
| + |
| + .changelog { white-space: pre-wrap; } |
| + select, input[type=text], input[type=search] { |
| + font: inherit; font-family: var(--sans); font-size: 0.9rem; color: var(--ink); background: var(--bg); |
| + border: 1px solid var(--rule); border-radius: 0.4rem; padding: 0.3rem 0.5rem; |
| + } |
| + td form { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; margin: 0; } |
| + |
| + form.inline { display: inline; margin: 0; } |
| + form.inline button { vertical-align: baseline; } |
| + |
| + form.listctl { display: flex; gap: 0.6rem; flex-wrap: wrap; margin: 0 0 0.6rem; } |
| + |
| + .icon { width: 1em; height: 1em; flex: none; vertical-align: -0.125em; } |
| + header { |
| + max-width: 64rem; margin: 0 auto; padding: 1.4rem 0; |
| + display: flex; align-items: baseline; gap: 1rem; |
| + border-bottom: 1px solid var(--rule); |
| + } |
| + header .brand a { font-weight: 700; color: var(--ink); text-decoration: none; } |
| + header nav { |
| + margin-left: auto; display: flex; align-items: baseline; gap: 1rem; |
| + font-size: 0.9rem; |
| + } |
| + header .addr { color: var(--muted); } |
| + |
| + header .switcher { |
| + position: relative; |
| + } |
| + header .switcher > summary { |
| + display: inline-flex; align-items: baseline; gap: 0.4rem; |
| + list-style: none; cursor: pointer; |
| + padding: 0.25rem 0.5rem; margin: -0.25rem -0.5rem; border-radius: 0.5rem; |
| + } |
| + header .switcher > summary::-webkit-details-marker { display: none; } |
| + header .switcher > summary:hover { background: var(--accent-soft); } |
| + |
| + header .switcher .current-name { |
| + font-weight: 700; min-width: 0; max-width: 14rem; |
| + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; |
| + } |
| + |
| + header .switcher > summary > .icon { color: var(--muted); align-self: center; width: 0.9em; } |
| + header .switcher[open] > summary > .icon { transform: rotate(180deg); } |
| + header .switcher-menu { |
| + position: absolute; z-index: 20; top: calc(100% + 0.6rem); left: -0.5rem; |
| + min-width: 13rem; max-width: 20rem; |
| + background: var(--bg); border: 1px solid var(--rule); border-radius: 0.6rem; |
| + padding: 0.35rem; font-size: 0.9rem; |
| + box-shadow: 0 0.4rem 1.5rem rgba(0, 0, 0, 0.14); |
| + } |
| + header .switcher-menu ul { list-style: none; margin: 0; padding: 0; } |
| + header .switcher-menu a, header .switcher-menu .current { |
| + display: flex; align-items: baseline; gap: 0.5rem; |
| + padding: 0.35rem 0.55rem; border-radius: 0.4rem; |
| + color: var(--ink); text-decoration: none; overflow-wrap: anywhere; |
| + } |
| + header .switcher-menu a:hover, header .switcher-menu a:focus { background: var(--accent-soft); } |
| + header .switcher-menu .current { font-weight: 600; background: var(--accent-soft); } |
| + |
| + header .switcher-menu .mark { |
| + color: var(--accent); width: 0.9rem; flex: none; |
| + display: flex; align-self: center; |
| + } |
| + header .switcher-menu a .mark { visibility: hidden; } |
| + header .switcher-menu .new a .mark { visibility: visible; } |
| + header .switcher-menu hr { border: 0; border-top: 1px solid var(--rule); margin: 0.35rem 0.2rem; } |
| + header .switcher-menu .new a { color: var(--accent); } |
| + main { max-width: 64rem; margin: 0 auto; padding: 2.5rem 0 3rem; min-height: 55vh; } |
| + h1 { font-size: 1.9rem; line-height: 1.15; margin: 0 0 1rem; } |
| + h2 { font-size: 1.25rem; margin: 2rem 0 0.75rem; } |
| + h3 { font-size: 1rem; margin: 1.5rem 0 0.5rem; } |
| + |
| + .settings-row { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; margin: 0 0 0.75rem; } |
| + .settings-layout { |
| + display: grid; grid-template-columns: minmax(10rem, 13rem) minmax(0, 1fr); |
| + gap: 2.5rem; align-items: start; |
| + } |
| + .settings-nav { display: flex; flex-direction: column; gap: 0.2rem; } |
| + .settings-nav a { |
| + display: block; padding: 0.45rem 0.65rem; border-radius: 0.35rem; |
| + color: var(--muted); text-decoration: none; |
| + } |
| + .settings-nav a:hover { color: var(--ink); background: var(--accent-soft); } |
| + .settings-nav a[aria-current="page"] { |
| + color: var(--ink); background: var(--accent-soft); font-weight: 600; |
| + } |
| + .settings-content { min-width: 0; } |
| + .settings-content > h2:first-child { margin-top: 0; } |
| + .danger-head { color: var(--bad); } |
| + p { max-width: 40rem; margin: 0 0 1rem; } |
| + .lede { font-size: 1.15rem; color: var(--muted); } |
| + .small { font-size: 0.9rem; color: var(--muted); } |
| + |
| + .visually-hidden { |
| + position: absolute; width: 1px; height: 1px; overflow: hidden; |
| + clip-path: inset(50%); white-space: nowrap; |
| + } |
| + .error { color: var(--bad); } |
| + .banner { |
| + background: var(--accent-soft); border-radius: 0.5rem; |
| + padding: 0.6rem 0.9rem; margin: 0 0 1.5rem; max-width: 40rem; |
| + } |
| + .tablewrap { overflow-x: auto; } |
| + .instances .instance-address { white-space: nowrap; } |
| + .instances td:nth-child(2) { white-space: nowrap; } |
| + .instance-open { display: inline-flex; padding: .25rem; } |
| + .instance-group th { text-align: left; padding-top: 1rem; background: var(--accent-soft); } |
| + .instance-group .small { font-weight: normal; } |
| + .small a[aria-current="true"] { color: var(--ink); font-weight: 700; text-decoration-thickness: 3px; } |
| + .instance-facts { display: grid; grid-template-columns: minmax(8rem, auto) 1fr; gap: .5rem 1.5rem; margin: 1rem 0; } |
| + .instance-facts dt { color: var(--muted); } |
| + .instance-facts dd { margin: 0; overflow-wrap: anywhere; } |
| + .instance-facts .cellspark { max-width: 24rem; } |
| + |
| + #logs-filter { display: flex; flex-wrap: wrap; align-items: end; gap: 0.65rem; margin-bottom: 1rem; } |
| + #logs-filter label { display: flex; flex-direction: column; gap: 0.25rem; max-width: 100%; } |
| + #logs-filter select, #logs-filter input { max-width: 100%; margin: 0; } |
| + #logs-filter select[name="host"] { max-width: 28rem; } |
| + .term { |
| + background: var(--accent-soft); border-radius: 0.5rem; |
| + padding: 0.8rem 0 0.8rem 1rem; margin: 0 0 1rem; |
| + min-height: 16rem; max-height: 70vh; overflow: auto; |
| + font-family: var(--mono); font-size: 0.8rem; line-height: 1.55; |
| + } |
| + |
| + .tl { white-space: pre; width: max-content; min-width: 100%; padding-right: 1rem; } |
| + |
| + .tl-t, .tl-h, .tl-f { color: var(--muted); } |
| + .tl-s { color: var(--accent); } |
| + |
| + .tl-warn .tl-m { color: var(--warn, #a6790f); } |
| + .tl-error .tl-m { color: var(--bad); font-weight: 600; } |
| + table { border-collapse: collapse; width: 100%; margin: 0 0 1rem; font-size: 0.95rem; } |
| + .tiles { display: flex; flex-wrap: wrap; gap: 1rem; margin: 0 0 1.5rem; } |
| + .tile { flex: 1 1 10rem; background: var(--accent-soft); border-radius: 0.6rem; padding: 1rem 1.1rem; } |
| + |
| + a.tile { color: var(--ink); text-decoration: none; } |
| + a.tile:hover { outline: 1px solid var(--accent); } |
| + |
| + .stat-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); } |
| + .tile h2 { |
| + margin: 0 0 0.35rem; font-family: var(--mono); font-size: 0.78rem; text-transform: uppercase; |
| + letter-spacing: 0.05em; color: var(--muted); font-weight: 600; |
| + } |
| + .tile-value { font-size: 1.6rem; font-weight: 600; margin: 0; } |
| + .tile-detail { color: var(--muted); font-size: 0.8rem; margin: 0.25rem 0 0; } |
| + |
| + .chip { |
| + display: inline-block; font-family: var(--mono); font-size: 0.78rem; font-weight: 500; |
| + padding: 0.15rem 0.55rem; border-radius: 999px; margin: 0.15rem 0.3rem 0.15rem 0; |
| + } |
| + .chip-stale { border: 1px solid var(--warn, #a6790f); color: var(--warn, #a6790f); } |
| + |
| + .chip-canary { border: 1px solid var(--accent); color: var(--accent); } |
| + .chip-prod { background: var(--bad); color: var(--bg); } |
| + |
| + .chip-expired { border: 1px solid var(--warn, #a6790f); color: var(--warn, #a6790f); } |
| + |
| + .chip-env { border: 1px solid var(--rule); color: var(--muted); } |
| + |
| + .chip-health-ok { border: 1px solid var(--good); color: var(--good); } |
| + .chip-health-attention { background: var(--warn, #a6790f); color: var(--bg); } |
| + .chip-health-notdeployed { border: 1px solid var(--rule); color: var(--muted); } |
| + .chip-health-unknown { border: 1px solid var(--warn, #a6790f); color: var(--warn, #a6790f); } |
| + |
| + .chip-freetier-inside { border: 1px solid var(--warn, #a6790f); color: var(--warn, #a6790f); } |
| + .chip-freetier-partial { border: 1px solid var(--accent); color: var(--accent); } |
| + .chip-freetier-paying { border: 1px solid var(--good); color: var(--good); } |
| + .chip-freetier-pooled { border: 1px solid var(--rule); color: var(--muted); } |
| + |
| + .chip-tier-unassigned { border: 1px solid var(--rule); color: var(--muted); } |
| + .chip-tier-try { border: 1px solid var(--rule); color: var(--muted); } |
| + .chip-tier-pool { border: 1px solid var(--rule); color: var(--muted); } |
| + .chip-tier-hosted { border: 1px solid var(--good); color: var(--good); } |
| + .chip-tier-fleet { border: 1px solid var(--accent); color: var(--accent); } |
| + .chip-tier-dedicated { border: 1px solid var(--warn, #a6790f); color: var(--warn, #a6790f); } |
| + .chip .icon { vertical-align: -0.1em; margin-right: 0.2em; } |
| + .chip-up { background: var(--accent-soft); color: #2f7a4f; } |
| + .chip-down { background: var(--accent-soft); color: var(--bad); } |
| + .chips { display: flex; flex-wrap: wrap; margin: 0 0 1rem; } |
| + |
| + .bar { display: inline-block; width: 6rem; height: 0.5rem; border-radius: 999px; background: var(--accent-soft); vertical-align: middle; overflow: hidden; } |
| + .bar-fill { display: block; height: 100%; background: var(--accent); } |
| + |
| + .knob-row { display: flex; gap: 0.6rem; align-items: center; margin: 0 0 0.5rem; flex-wrap: wrap; } |
| + .knob-row label { min-width: 12rem; color: var(--muted); font-size: 0.9rem; } |
| + .knob-row input[type=number], .knob-row input[type=text] { width: 8rem; } |
| + .knob-row input[type=range] { width: 12rem; } |
| + |
| + .flow { margin: 0 0 1.75rem; } |
| + .flow-line { |
| + --flow-y: 0.52rem; |
| + display: flex; flex-wrap: wrap; gap: 1.1rem 2.6rem; |
| + list-style: none; margin: 0; padding: 0; |
| + } |
| + .flow-stage { position: relative; flex: 1 1 10rem; min-width: 9rem; } |
| + .flow-stage + .flow-stage::before, .flow-stage + .flow-stage::after { |
| + content: ""; position: absolute; pointer-events: none; |
| + } |
| + .flow-stage + .flow-stage::before { |
| + left: -2.05rem; top: var(--flow-y); width: 1.3rem; border-top: 1px dashed var(--rule); |
| + } |
| + .flow-stage + .flow-stage::after { |
| + left: -0.82rem; top: var(--flow-y); transform: translateY(-50%); |
| + border: 0.28rem solid transparent; border-right: 0; border-left-color: var(--rule); |
| + } |
| + .flow-stage.is-carried::before { border-top-style: solid; border-top-color: var(--accent); } |
| + .flow-stage.is-carried::after { border-left-color: var(--accent); } |
| + .flow-label { |
| + margin: 0; font-family: var(--mono); font-size: 0.74rem; text-transform: uppercase; |
| + letter-spacing: 0.05em; color: var(--muted); font-weight: 600; |
| + } |
| + .flow-version { margin: 0.3rem 0 0; font-size: 0.95rem; } |
| + |
| + .flow-stage:not(.is-held) .flow-version { color: var(--muted); } |
| + .flow-note { margin: 0.25rem 0 0; font-size: 0.78rem; color: var(--muted); } |
| + .flow-addrs { |
| + list-style: none; margin: 0.6rem 0 0; padding: 0 0 0 0.7rem; |
| + border-left: 1px solid var(--rule); display: grid; gap: 0.35rem; |
| + } |
| + .flow-addr { font-size: 0.82rem; line-height: 1.35; } |
| + .flow-addr a { display: block; overflow-wrap: anywhere; } |
| + .flow-health { display: inline-flex; align-items: center; gap: 0.2rem; color: var(--muted); } |
| + .flow-health .icon { width: 0.9em; height: 0.9em; flex: none; } |
| + .flow-tone-ok .flow-health { color: var(--good); } |
| + .flow-tone-wait .flow-health { color: var(--warn, #a6790f); } |
| + .flow-tone-bad .flow-health { color: var(--bad); font-weight: 600; } |
| + .flow-band { margin: 1.4rem 0 0.7rem; font-size: 0.9rem; color: var(--muted); } |
| + .flow-caption { margin: 1.1rem 0 0; } |
| + |
| + @media (max-width: 46rem) { |
| + .settings-layout { display: block; } |
| + .settings-nav { |
| + flex-direction: row; overflow-x: auto; margin: 0 0 1.5rem; |
| + padding-bottom: 0.25rem; |
| + } |
| + .settings-nav a { white-space: nowrap; } |
| + .flow-line { display: block; } |
| + .flow-stage + .flow-stage { margin-top: 1.6rem; } |
| + .flow-stage + .flow-stage::before { |
| + left: 0.55rem; top: -1.2rem; width: 0; height: 0.85rem; |
| + border-top: 0; border-left: 1px dashed var(--rule); |
| + } |
| + .flow-stage + .flow-stage::after { |
| + left: 0.55rem; top: -0.34rem; transform: translateX(-50%); |
| + border: 0.28rem solid transparent; border-bottom: 0; border-top-color: var(--rule); |
| + border-left-color: transparent; |
| + } |
| + .flow-stage.is-carried::before { border-left-color: var(--accent); } |
| + .flow-stage.is-carried::after { border-top-color: var(--accent); border-left-color: transparent; } |
| + } |
| + |
| + .ladder { list-style: none; margin: 0 0 1rem; padding: 0; max-width: 40rem; } |
| + .rung { |
| + border: 1px solid var(--rule); border-radius: 0.6rem; padding: 0.8rem 1rem; |
| + } |
| + .rung + .rung { margin-top: 0.4rem; } |
| + .rung-name { |
| + font-family: var(--mono); |
| + font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; |
| + font-weight: 600; color: var(--muted); margin: 0 0 0.3rem; |
| + } |
| + .rung p { margin: 0 0 0.2rem; } |
| + .rung .gate { margin: 0.5rem 0 0; } |
| + .rung .routes { list-style: none; margin: 0.3rem 0 0.6rem; padding: 0; font-size: 0.95rem; } |
| + .rung .routes li { padding: 0.1rem 0; } |
| + |
| + .step { |
| + display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; |
| + margin: 0.7rem -1rem -0.8rem; padding: 0.5rem 1rem; |
| + border-top: 1px solid var(--rule); |
| + } |
| + .step form { display: flex; align-items: baseline; gap: 0.5rem; margin: 0; } |
| + .step > .icon { color: var(--muted); align-self: center; } |
| + th { |
| + font-family: var(--mono); |
| + font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; |
| + color: var(--muted); text-align: left; font-weight: 600; |
| + } |
| + th, td { padding: 0.5rem 1rem 0.5rem 0; border-bottom: 1px solid var(--rule); } |
| + form.signin { display: flex; gap: 0.6rem; flex-wrap: wrap; margin: 0 0 1rem; } |
| + form.signin input[type=text] { padding: 0.55rem 0.8rem; min-width: 16rem; } |
| + button, .btn { |
| + font-size: 0.95rem; font-weight: 600; background: var(--accent); |
| + color: var(--bg); border: 0; border-radius: 0.4rem; |
| + padding: 0.6rem 1.2rem; cursor: pointer; |
| + text-decoration: none; display: inline-block; |
| + } |
| + button.linkish { |
| + background: none; color: var(--accent); font-weight: 400; padding: 0; |
| + border-radius: 0; text-decoration: underline; font-size: 0.9rem; cursor: pointer; |
| + } |
| + button:not(.linkish):hover, .btn:hover { background: var(--accent-strong); } |
| + button.linkish.danger { color: var(--bad); } |
| + |
| + .btn-sm, button.btn-sm { |
| + font-size: 0.85rem; padding: 0.35rem 0.8rem; border-radius: 0.4rem; |
| + } |
| + button.btn-danger, .btn-danger { background: var(--bad); color: var(--bg); } |
| + button.btn-danger:hover, .btn-danger:hover { background: var(--bad); filter: brightness(0.9); } |
| + |
| + td .cell-value { display: inline-block; min-width: 3.5rem; font-weight: 600; } |
| + .cellspark { width: 6rem; height: 1.1rem; vertical-align: -0.2rem; color: var(--muted); } |
| + .cellspark polyline { stroke-width: 2.5; } |
| + |
| + .feed { list-style: none; margin: 0 0 0.5rem; padding: 0; max-width: 40rem; } |
| + .feed li { padding: 0.25rem 0; border-bottom: 1px solid var(--rule); } |
| + .feed li:last-child { border-bottom: 0; } |
| + |
| + .confirm-card { |
| + border: 1px solid var(--rule); border-radius: 0.6rem; |
| + padding: 1.25rem 1.4rem; max-width: 40rem; margin: 0 0 1rem; |
| + } |
| + .confirm-card .actions { display: flex; gap: 0.9rem; align-items: center; margin-top: 1rem; } |
| + .confirm-card .confirm-note { margin: 0.8rem 0 0; } |
| + |
| + |
| + .archwrap { overflow-x: auto; margin: 0.2rem 0 1.6rem; } |
| + |
| + h4.archband { |
| + border-top: 1px solid var(--rule); color: var(--muted); |
| + font: 600 0.8rem var(--mono); letter-spacing: 0.02em; |
| + margin: 1.5rem 0 0; padding-top: 0.5rem; |
| + } |
| + |
| + .archsvg { display: block; } |
| + .archsvg rect { fill: var(--accent-soft); stroke: var(--rule); } |
| + .archsvg .archnode-box rect { stroke: var(--accent); } |
| + .archsvg .archtitle { fill: var(--ink); font: 600 13px var(--sans); } |
| + .archsvg .archsub { fill: var(--muted); font: 400 11px var(--sans); } |
| + |
| + .archsvg .archedge { fill: none; stroke: var(--muted); stroke-width: 1.5; } |
| + |
| + .archsvg .archalarm { fill: var(--good); } |
| + .archsvg .archalarm-firing { fill: var(--bad); } |
| + |
| + .archbox { |
| + border: 1px solid var(--rule); border-radius: 0.6rem; |
| + padding: 0.9rem 1.1rem; margin: 0 0 0.6rem; max-width: 64rem; |
| + } |
| + .archbox h3 { margin: 0 0 0.3rem; } |
| + .archbox p { margin: 0 0 0.2rem; } |
| + |
| + .archbox .fact + .fact::before { content: " · "; color: var(--muted); } |
| + footer { |
| + max-width: 64rem; margin: 0 auto; padding: 1.25rem 0 2rem; |
| + border-top: 1px solid var(--rule); |
| + font-size: 0.85rem; color: var(--muted); |
| + } |
| + |
| + .pagehead { margin: 0 0 1.75rem; } |
| + .eyebrow { |
| + font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.08em; |
| + text-transform: uppercase; color: var(--muted); margin: 0 0 0.3rem; |
| + } |
| + .pagehead h1 { margin: 0 0 0.35rem; } |
| + .pagehead .lede { margin: 0; font-size: 1.05rem; } |
| + |
| + .reveal > summary { font-size: 1.1rem; font-weight: 600; cursor: pointer; } |
| + |
| + .tabs { |
| + display: flex; flex-wrap: wrap; gap: 1.25rem; margin: 0 0 2rem; |
| + border-bottom: 1px solid var(--rule); |
| + font-family: var(--mono); font-size: 0.85rem; |
| + } |
| + .tabs a { |
| + color: var(--muted); text-decoration: none; |
| + padding: 0 0 0.55rem; border-bottom: 2px solid transparent; margin-bottom: -1px; |
| + } |
| + .tabs a:hover { color: var(--ink); } |
| + .tabs a[aria-current] { color: var(--ink); border-bottom-color: var(--accent); } |
| + header nav a[aria-current] { color: var(--ink); font-weight: 600; text-decoration: none; } |
| + header nav a.addr { color: var(--muted); text-decoration: none; } |
| + header nav a.addr:hover { text-decoration: underline; } |
| +</style> |
| +<style>.preview-state{border-top:3px dashed var(--accent);margin:3rem 0 0;padding-top:1rem}.preview-label{font:600 .85rem/1.4 var(--sans);color:var(--accent);text-transform:uppercase;letter-spacing:.04em;max-width:64rem;margin:0 auto .5rem}</style></head> |
| +<body> |
| +<header> |
| + |
| + <details class="switcher" id="switcher"> |
| + <summary> |
| + <span class="small">Account</span> |
| + <span class="current-name">ops</span> |
| + <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg> |
| + </summary> |
| + <div class="switcher-menu"> |
| + <ul> |
| + <li> |
| + <span class="current" aria-current="true"><span class="mark"><svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg></span>ops</span> |
| + </li> |
| + |
| + </ul> |
| + <hr> |
| + <div class="new"><a href="/accounts/new"><span class="mark"><svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg></span>New account</a></div> |
| + </div> |
| + </details> |
| + |
| + <nav> |
| + |
| + <a href="/a/ops/">Apps</a> |
| + <a href="/a/ops/fleet">Fleet</a> |
| + <a href="/a/ops/team">Team</a> |
| + <a href="/a/ops/billing" aria-current="page" data-copy="billing.nav">Billing</a> |
| + <a href="/a/ops/settings">Settings</a> |
| + |
| + <a class="addr" href="/settings" title="Your settings">paul@keymail.dev</a> |
| + <form method="post" action="/auth/signout"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <button class="linkish" type="submit">Sign out</button> |
| + </form> |
| + </nav> |
| +</header> |
| +<section class="preview-state"><p class="preview-label">Preview state: Owner on Free, prices quoted for Ireland</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.current.free">Free. One member, 3 apps, 100 awake hours a month, 250 MB of storage and 1 GB of transfer. Apps sleep after 2 minutes idle.</span></p> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.choose.heading">Choose a plan</span></h2> |
| +<p class="small"><span data-copy="billing.choose.lede">Prices are per year, before VAT, and depend on your billing country. The exact price is shown before you pay.</span></p> |
| + |
| +<form class="signin" method="get" action="/a/bab/billing"> |
| + <label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country"> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE" selected>Ireland</option> |
| + |
| + </select> |
| + </label> |
| + <button class="linkish" type="submit"><span data-copy="billing.quote.button">Show prices for this country</span></button> |
| +</form> |
| + |
| +<p class="small">Prices for Ireland are 100% of the base price, and so are the monthly inclusions.</p> |
| + |
| +<form method="post" action="/a/bab/billing/checkout"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <div class="tablewrap"> |
| + <table> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="personal" required> |
| + <span data-copy="billing.plan.personal.name">Personal</span></label></td> |
| + <td><strong>€24</strong> <span data-copy="billing.price.peryear">a year</span></td> |
| + <td class="small"><span data-copy="billing.plan.personal.blurb">One member. Each month: 85 awake hours, 2 GB storage, 2 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="community" required> |
| + <span data-copy="billing.plan.community.name">Community</span></label></td> |
| + <td><strong>€5</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€50 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.community.blurb">Per member, minimum 10. Each member adds, per month: 17 awake hours, 0.5 GB storage, 0.4 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="business" required> |
| + <span data-copy="billing.plan.business.name">Business</span></label></td> |
| + <td><strong>€50</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€500 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.business.blurb">Per member, minimum 10. Each member adds, per month: 170 awake hours, 5 GB storage, 3 GB transfer, 30,000 replication writes. Priority support.</span></td> |
| + </tr> |
| + |
| + </table> |
| + </div> |
| + <p class="small"><span data-copy="billing.plan.community.name">Community</span> and <span data-copy="billing.plan.business.name">Business</span> are priced per member. You have 1 members; the minimum is 10.</p> |
| + <p><label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country" required> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE" selected>Ireland</option> |
| + |
| + </select> |
| + </label></p> |
| + <p><label><span data-copy="billing.postal.label">Postal code</span> |
| + <input type="text" name="postal" autocomplete="postal-code"> |
| + </label></p> |
| + <p class="small"><span data-copy="billing.postal.help">Needed in the United States, Canada and India, where tax depends on it. Leave it empty elsewhere. We do not keep it.</span></p> |
| + <p><label><input type="checkbox" name="consent" value="2026-09-07-v1" required> |
| + I have read <a href="https://carloku.com/pricing/">the pricing page</a> and agree to be charged yearly at the price shown at checkout, plus usage beyond the plan's inclusions billed monthly.</label></p> |
| + <button type="submit"><span data-copy="billing.pay.button">Continue to payment</span></button> |
| + <p class="small"><span data-copy="billing.pay.note">You pay by card at Stripe. Nothing is charged until you confirm there.</span></p> |
| +</form> |
| + |
| + |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: After a failed checkout attempt (error=unavailable)</p><main> |
| + |
| + |
| + |
| + |
| + |
| +<p class="banner error"><span data-copy="billing.error.unavailable">Billing is not available right now. Nothing was charged. Try again in a few minutes.</span></p> |
| + |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.current.free">Free. One member, 3 apps, 100 awake hours a month, 250 MB of storage and 1 GB of transfer. Apps sleep after 2 minutes idle.</span></p> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.choose.heading">Choose a plan</span></h2> |
| +<p class="small"><span data-copy="billing.choose.lede">Prices are per year, before VAT, and depend on your billing country. The exact price is shown before you pay.</span></p> |
| + |
| +<form class="signin" method="get" action="/a/bab/billing"> |
| + <label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country"> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label> |
| + <button class="linkish" type="submit"><span data-copy="billing.quote.button">Show prices for this country</span></button> |
| +</form> |
| + |
| + |
| + |
| +<form method="post" action="/a/bab/billing/checkout"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <div class="tablewrap"> |
| + <table> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="personal" required> |
| + <span data-copy="billing.plan.personal.name">Personal</span></label></td> |
| + <td><strong>€24</strong> <span data-copy="billing.price.peryear">a year</span></td> |
| + <td class="small"><span data-copy="billing.plan.personal.blurb">One member. Each month: 85 awake hours, 2 GB storage, 2 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="community" required> |
| + <span data-copy="billing.plan.community.name">Community</span></label></td> |
| + <td><strong>€5</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€50 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.community.blurb">Per member, minimum 10. Each member adds, per month: 17 awake hours, 0.5 GB storage, 0.4 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="business" required> |
| + <span data-copy="billing.plan.business.name">Business</span></label></td> |
| + <td><strong>€50</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€500 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.business.blurb">Per member, minimum 10. Each member adds, per month: 170 awake hours, 5 GB storage, 3 GB transfer, 30,000 replication writes. Priority support.</span></td> |
| + </tr> |
| + |
| + </table> |
| + </div> |
| + <p class="small"><span data-copy="billing.plan.community.name">Community</span> and <span data-copy="billing.plan.business.name">Business</span> are priced per member. You have 1 members; the minimum is 10.</p> |
| + <p><label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country" required> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label></p> |
| + <p><label><span data-copy="billing.postal.label">Postal code</span> |
| + <input type="text" name="postal" autocomplete="postal-code"> |
| + </label></p> |
| + <p class="small"><span data-copy="billing.postal.help">Needed in the United States, Canada and India, where tax depends on it. Leave it empty elsewhere. We do not keep it.</span></p> |
| + <p><label><input type="checkbox" name="consent" value="2026-09-07-v1" required> |
| + I have read <a href="https://carloku.com/pricing/">the pricing page</a> and agree to be charged yearly at the price shown at checkout, plus usage beyond the plan's inclusions billed monthly.</label></p> |
| + <button type="submit"><span data-copy="billing.pay.button">Continue to payment</span></button> |
| + <p class="small"><span data-copy="billing.pay.note">You pay by card at Stripe. Nothing is charged until you confirm there.</span></p> |
| +</form> |
| + |
| + |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: A second checkout while one is settling (error=pending)</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| +<p class="banner error"><span data-copy="billing.error.pending">An earlier checkout for this account is still completing. Wait a minute and reload this page before trying again.</span></p> |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.current.free">Free. One member, 3 apps, 100 awake hours a month, 250 MB of storage and 1 GB of transfer. Apps sleep after 2 minutes idle.</span></p> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.choose.heading">Choose a plan</span></h2> |
| +<p class="small"><span data-copy="billing.choose.lede">Prices are per year, before VAT, and depend on your billing country. The exact price is shown before you pay.</span></p> |
| + |
| +<form class="signin" method="get" action="/a/bab/billing"> |
| + <label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country"> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label> |
| + <button class="linkish" type="submit"><span data-copy="billing.quote.button">Show prices for this country</span></button> |
| +</form> |
| + |
| + |
| + |
| +<form method="post" action="/a/bab/billing/checkout"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <div class="tablewrap"> |
| + <table> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="personal" required> |
| + <span data-copy="billing.plan.personal.name">Personal</span></label></td> |
| + <td><strong>€24</strong> <span data-copy="billing.price.peryear">a year</span></td> |
| + <td class="small"><span data-copy="billing.plan.personal.blurb">One member. Each month: 85 awake hours, 2 GB storage, 2 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="community" required> |
| + <span data-copy="billing.plan.community.name">Community</span></label></td> |
| + <td><strong>€5</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€50 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.community.blurb">Per member, minimum 10. Each member adds, per month: 17 awake hours, 0.5 GB storage, 0.4 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="business" required> |
| + <span data-copy="billing.plan.business.name">Business</span></label></td> |
| + <td><strong>€50</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€500 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.business.blurb">Per member, minimum 10. Each member adds, per month: 170 awake hours, 5 GB storage, 3 GB transfer, 30,000 replication writes. Priority support.</span></td> |
| + </tr> |
| + |
| + </table> |
| + </div> |
| + <p class="small"><span data-copy="billing.plan.community.name">Community</span> and <span data-copy="billing.plan.business.name">Business</span> are priced per member. You have 1 members; the minimum is 10.</p> |
| + <p><label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country" required> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label></p> |
| + <p><label><span data-copy="billing.postal.label">Postal code</span> |
| + <input type="text" name="postal" autocomplete="postal-code"> |
| + </label></p> |
| + <p class="small"><span data-copy="billing.postal.help">Needed in the United States, Canada and India, where tax depends on it. Leave it empty elsewhere. We do not keep it.</span></p> |
| + <p><label><input type="checkbox" name="consent" value="2026-09-07-v1" required> |
| + I have read <a href="https://carloku.com/pricing/">the pricing page</a> and agree to be charged yearly at the price shown at checkout, plus usage beyond the plan's inclusions billed monthly.</label></p> |
| + <button type="submit"><span data-copy="billing.pay.button">Continue to payment</span></button> |
| + <p class="small"><span data-copy="billing.pay.note">You pay by card at Stripe. Nothing is charged until you confirm there.</span></p> |
| +</form> |
| + |
| + |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: The account changed under the form (error=conflict)</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<p class="banner error"><span data-copy="billing.error.conflict">The account changed while you were on this page, for example a member was added. Check the plan and try again.</span></p> |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.current.free">Free. One member, 3 apps, 100 awake hours a month, 250 MB of storage and 1 GB of transfer. Apps sleep after 2 minutes idle.</span></p> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.choose.heading">Choose a plan</span></h2> |
| +<p class="small"><span data-copy="billing.choose.lede">Prices are per year, before VAT, and depend on your billing country. The exact price is shown before you pay.</span></p> |
| + |
| +<form class="signin" method="get" action="/a/bab/billing"> |
| + <label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country"> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label> |
| + <button class="linkish" type="submit"><span data-copy="billing.quote.button">Show prices for this country</span></button> |
| +</form> |
| + |
| + |
| + |
| +<form method="post" action="/a/bab/billing/checkout"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <div class="tablewrap"> |
| + <table> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="personal" required> |
| + <span data-copy="billing.plan.personal.name">Personal</span></label></td> |
| + <td><strong>€24</strong> <span data-copy="billing.price.peryear">a year</span></td> |
| + <td class="small"><span data-copy="billing.plan.personal.blurb">One member. Each month: 85 awake hours, 2 GB storage, 2 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="community" required> |
| + <span data-copy="billing.plan.community.name">Community</span></label></td> |
| + <td><strong>€5</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€50 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.community.blurb">Per member, minimum 10. Each member adds, per month: 17 awake hours, 0.5 GB storage, 0.4 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="business" required> |
| + <span data-copy="billing.plan.business.name">Business</span></label></td> |
| + <td><strong>€50</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€500 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.business.blurb">Per member, minimum 10. Each member adds, per month: 170 awake hours, 5 GB storage, 3 GB transfer, 30,000 replication writes. Priority support.</span></td> |
| + </tr> |
| + |
| + </table> |
| + </div> |
| + <p class="small"><span data-copy="billing.plan.community.name">Community</span> and <span data-copy="billing.plan.business.name">Business</span> are priced per member. You have 1 members; the minimum is 10.</p> |
| + <p><label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country" required> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label></p> |
| + <p><label><span data-copy="billing.postal.label">Postal code</span> |
| + <input type="text" name="postal" autocomplete="postal-code"> |
| + </label></p> |
| + <p class="small"><span data-copy="billing.postal.help">Needed in the United States, Canada and India, where tax depends on it. Leave it empty elsewhere. We do not keep it.</span></p> |
| + <p><label><input type="checkbox" name="consent" value="2026-09-07-v1" required> |
| + I have read <a href="https://carloku.com/pricing/">the pricing page</a> and agree to be charged yearly at the price shown at checkout, plus usage beyond the plan's inclusions billed monthly.</label></p> |
| + <button type="submit"><span data-copy="billing.pay.button">Continue to payment</span></button> |
| + <p class="small"><span data-copy="billing.pay.note">You pay by card at Stripe. Nothing is charged until you confirm there.</span></p> |
| +</form> |
| + |
| + |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: Back from Stripe, webhook not yet arrived (pending=1)</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<p class="banner"><span data-copy="billing.current.pending">Thanks. Stripe is confirming your payment. Your plan turns on within a few minutes, and this page will show it.</span></p> |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.current.free">Free. One member, 3 apps, 100 awake hours a month, 250 MB of storage and 1 GB of transfer. Apps sleep after 2 minutes idle.</span></p> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.choose.heading">Choose a plan</span></h2> |
| +<p class="small"><span data-copy="billing.choose.lede">Prices are per year, before VAT, and depend on your billing country. The exact price is shown before you pay.</span></p> |
| + |
| +<form class="signin" method="get" action="/a/bab/billing"> |
| + <label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country"> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label> |
| + <button class="linkish" type="submit"><span data-copy="billing.quote.button">Show prices for this country</span></button> |
| +</form> |
| + |
| + |
| + |
| +<form method="post" action="/a/bab/billing/checkout"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <div class="tablewrap"> |
| + <table> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="personal" required> |
| + <span data-copy="billing.plan.personal.name">Personal</span></label></td> |
| + <td><strong>€24</strong> <span data-copy="billing.price.peryear">a year</span></td> |
| + <td class="small"><span data-copy="billing.plan.personal.blurb">One member. Each month: 85 awake hours, 2 GB storage, 2 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="community" required> |
| + <span data-copy="billing.plan.community.name">Community</span></label></td> |
| + <td><strong>€5</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€50 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.community.blurb">Per member, minimum 10. Each member adds, per month: 17 awake hours, 0.5 GB storage, 0.4 GB transfer.</span></td> |
| + </tr> |
| + |
| + <tr> |
| + <td><label><input type="radio" name="plan" value="business" required> |
| + <span data-copy="billing.plan.business.name">Business</span></label></td> |
| + <td><strong>€50</strong> <span data-copy="billing.price.permember">per member a year</span> |
| + <br><span class="small">€500 <span data-copy="billing.price.peryear">a year</span></span></td> |
| + <td class="small"><span data-copy="billing.plan.business.blurb">Per member, minimum 10. Each member adds, per month: 170 awake hours, 5 GB storage, 3 GB transfer, 30,000 replication writes. Priority support.</span></td> |
| + </tr> |
| + |
| + </table> |
| + </div> |
| + <p class="small"><span data-copy="billing.plan.community.name">Community</span> and <span data-copy="billing.plan.business.name">Business</span> are priced per member. You have 1 members; the minimum is 10.</p> |
| + <p><label><span data-copy="billing.country.label">Billing country</span> |
| + <select name="country" required> |
| + <option value="" data-copy="billing.country.placeholder">Choose a country</option> |
| + <option value="IE">Ireland</option> |
| + |
| + </select> |
| + </label></p> |
| + <p><label><span data-copy="billing.postal.label">Postal code</span> |
| + <input type="text" name="postal" autocomplete="postal-code"> |
| + </label></p> |
| + <p class="small"><span data-copy="billing.postal.help">Needed in the United States, Canada and India, where tax depends on it. Leave it empty elsewhere. We do not keep it.</span></p> |
| + <p><label><input type="checkbox" name="consent" value="2026-09-07-v1" required> |
| + I have read <a href="https://carloku.com/pricing/">the pricing page</a> and agree to be charged yearly at the price shown at checkout, plus usage beyond the plan's inclusions billed monthly.</label></p> |
| + <button type="submit"><span data-copy="billing.pay.button">Continue to payment</span></button> |
| + <p class="small"><span data-copy="billing.pay.note">You pay by card at Stripe. Nothing is charged until you confirm there.</span></p> |
| +</form> |
| + |
| + |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: Active Personal subscription, portal available</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.plan.personal.name">Personal</span>, 1 members, renews on 2027-09-30.</p> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.manage.heading">Cards, invoices and cancelling</span></h2> |
| +<p class="small"><span data-copy="billing.manage.body">Change your card, download invoices or cancel at Stripe. Cancelling keeps the plan until the end of the year you paid for.</span></p> |
| +<form method="post" action="/a/bab/billing/portal"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <button type="submit"><span data-copy="billing.manage.button">Open billing at Stripe</span></button> |
| +</form> |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: Payment failed on renewal (past_due)</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.plan.personal.name">Personal</span>, 1 members, renews on 2027-09-30.</p> |
| + |
| + |
| +<p class="banner error"><span data-copy="billing.current.pastdue">The last payment did not go through. Update your card below to keep the plan.</span></p> |
| + |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.manage.heading">Cards, invoices and cancelling</span></h2> |
| +<p class="small"><span data-copy="billing.manage.body">Change your card, download invoices or cancel at Stripe. Cancelling keeps the plan until the end of the year you paid for.</span></p> |
| +<form method="post" action="/a/bab/billing/portal"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <button type="submit"><span data-copy="billing.manage.button">Open billing at Stripe</span></button> |
| +</form> |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: An operator review hold is open</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.plan.personal.name">Personal</span>, 1 members, renews on 2027-09-30.</p> |
| + |
| + |
| +<p class="banner error"><span data-copy="billing.current.pastdue">The last payment did not go through. Update your card below to keep the plan.</span></p> |
| +<p class="banner"><span data-copy="billing.current.review">Something about this account's billing needs a person to look at it. Nothing is charged while that happens. We will be in touch.</span></p> |
| + |
| + |
| + |
| + |
| + |
| +<h2><span data-copy="billing.manage.heading">Cards, invoices and cancelling</span></h2> |
| +<p class="small"><span data-copy="billing.manage.body">Change your card, download invoices or cancel at Stripe. Cancelling keeps the plan until the end of the year you paid for.</span></p> |
| +<form method="post" action="/a/bab/billing/portal"> |
| + <input type="hidden" name="csrf" value="ZMN9j5wRsWbKVAXM9H5YrfbyML9QQvNlVAtQB_JE8NQ"> |
| + <button type="submit"><span data-copy="billing.manage.button">Open billing at Stripe</span></button> |
| +</form> |
| + |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: A member who is not an owner</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.plan.personal.name">Personal</span>, 1 members, renews on 2027-09-30.</p> |
| + |
| + |
| +<p class="banner error"><span data-copy="billing.current.pastdue">The last payment did not go through. Update your card below to keep the plan.</span></p> |
| +<p class="banner"><span data-copy="billing.current.review">Something about this account's billing needs a person to look at it. Nothing is charged while that happens. We will be in touch.</span></p> |
| + |
| + |
| +<p class="small"><span data-copy="billing.member.readonly">Only an account owner can change the plan.</span></p> |
| + |
| + |
| + |
| +</main></section> |
| +<section class="preview-state"><p class="preview-label">Preview state: Billing switched off on this deployment (production today)</p><main> |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| + |
| +<div class="pagehead"> |
| + <p class="eyebrow">Account · ops</p> |
| + <h1>Billing</h1> |
| + <p class="lede"><span data-copy="billing.lede">Your plan, what it includes, and how to change it.</span></p> |
| +</div> |
| + |
| +<h2><span data-copy="billing.current.heading">Your plan</span></h2> |
| + |
| +<p><span data-copy="billing.current.free">Free. One member, 3 apps, 100 awake hours a month, 250 MB of storage and 1 GB of transfer. Apps sleep after 2 minutes idle.</span></p> |
| + |
| + |
| + |
| + |
| + |
| + |
| +<p><span data-copy="billing.closed">Paid plans are not open on this console yet. Every account is on the Free plan.</span></p> |
| + |
| + |
| + |
| +</main></section></body> |
| +</html> |