fix(tier2-g): explain the Gate clearly, kill the "everyone outside = fraud" misconception
The previous pass made the labels friendlier but didn't actually explain what the Gate does. A real user (correctly) read "trusted networks" as "any guest outside my IP is treated as fraud," which is the opposite of what's happening — and missed the entire value prop: stopping forwarded invitation links from being used by uninvited people. That's the central pain GuestGuard solves; the Gate page has to lead with it, not bury it under controls. What changed on the Gate card: - Big value-prop block at the top in plain language: "Every guest gets their own personal invitation link. The Gate watches each link and stops forwarded or shared invitations from being used by people who weren't on your list." Also explicit: "Real invited guests don't notice it. You don't need to set anything up — the Gate is on by default with sensible settings." - New "How does the Gate work?" collapsible explainer with a 4-step walkthrough using Aunty Patience as the protagonist. Covers what signals the Gate looks at, when guests *do* get flagged, and the reassurance that normal day-to-day variation (Wi-Fi → mobile data, changing rooms) doesn't trigger anything. - Preset descriptions rewritten to talk about the actual pain instead of generic strictness levels. "Stops forwarded links from being used by people who weren't invited" lands much harder than "Recommended for most parties." - Trusted networks section opens with an explicit "What this is and isn't" panel that directly addresses the misconception: adding a network here is a *speed-up* for guests on your Wi-Fi, NOT a whitelist that classifies everyone else as suspicious. Empty state reads "No trusted networks — and that's fine. The Gate is doing its job" so a host doesn't feel they've missed a setup step. - Pill on the section header tags it "Optional · most hosts don't need this" to drop its prominence. The button copy went from "Use my current network" (which read like "approve myself") to "Trust the network I'm on right now." No backend changes. Internal Go names (FraudThresholds, /security/* endpoints) untouched — never user-visible, renaming would churn for zero end-user benefit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -50,10 +50,30 @@ interface Preset {
|
|||||||
block: number
|
block: number
|
||||||
}
|
}
|
||||||
const PRESETS: Preset[] = [
|
const PRESETS: Preset[] = [
|
||||||
{ id: 'relaxed', label: 'Relaxed', description: 'Only stop obvious gate-crashers.', medium: 50, high: 80, block: 95 },
|
{
|
||||||
{ id: 'balanced', label: 'Balanced', description: 'Recommended for most parties and gatherings.', medium: 30, high: 60, block: 85 },
|
id: 'relaxed',
|
||||||
{ id: 'strict', label: 'Strict', description: 'Tight check — good for weddings and private events.', medium: 25, high: 50, block: 70 },
|
label: 'Relaxed',
|
||||||
{ id: 'very_strict', label: 'Very strict', description: 'VIP or high-profile guest list.', medium: 15, high: 35, block: 55 },
|
description: "Casual party — fine if friends share their link with each other.",
|
||||||
|
medium: 50, high: 80, block: 95,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'balanced',
|
||||||
|
label: 'Balanced',
|
||||||
|
description: "Recommended. Stops forwarded links from being used by people who weren't invited, without nagging anyone normal.",
|
||||||
|
medium: 30, high: 60, block: 85,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'strict',
|
||||||
|
label: 'Strict',
|
||||||
|
description: "Wedding or private event — uninvited plus-ones are a problem and the guest list matters.",
|
||||||
|
medium: 25, high: 50, block: 70,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'very_strict',
|
||||||
|
label: 'Very strict',
|
||||||
|
description: "VIP or high-profile guest list. Even small mismatches get flagged for your review.",
|
||||||
|
medium: 15, high: 35, block: 55,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -247,14 +267,89 @@ function verdictLabel(v: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<header class="mb-1 flex items-center justify-between">
|
<header class="mb-3 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
<h2 class="text-lg font-semibold">Gate</h2>
|
<h2 class="text-lg font-semibold">Gate</h2>
|
||||||
|
<p class="text-xs text-zinc-500">Keeps your guest list yours.</p>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<p class="mb-5 text-sm text-zinc-400">
|
|
||||||
The gate watches every guest arriving via your invitation links. Tell it how strict to be,
|
<!-- Value-prop paragraph. This is the *point* of the feature in plain
|
||||||
let it know about networks you trust, and it'll wave through anyone normal while flagging
|
words. The user named the actual pain ("preventing multiple sharing
|
||||||
anything that looks like an uninvited crash.
|
of events to uninvited guests") — that phrase lives here verbatim. -->
|
||||||
|
<div class="mb-4 rounded-lg border border-brand-700/40 bg-brand-500/[0.04] p-4 text-sm leading-relaxed">
|
||||||
|
<p class="text-zinc-200">
|
||||||
|
Every guest gets their own personal invitation link — only meant for them.
|
||||||
|
The <strong class="text-brand-300">Gate</strong> watches each link in the
|
||||||
|
background and stops <strong>forwarded or shared invitations</strong>
|
||||||
|
from being used by people who weren't on your list.
|
||||||
</p>
|
</p>
|
||||||
|
<p class="mt-2 text-xs text-zinc-400">
|
||||||
|
Real invited guests don't notice it. You don't need to set anything up
|
||||||
|
for it to work — the Gate is on by default with sensible settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- "How does this work?" — collapsed by default so the page stays
|
||||||
|
scannable. The expander walks through the actual mechanism in
|
||||||
|
plain language so curious / sceptical hosts can see what we're
|
||||||
|
doing without reading docs. -->
|
||||||
|
<details class="group mb-6 rounded-lg border border-zinc-800 bg-zinc-950">
|
||||||
|
<summary class="flex cursor-pointer items-center justify-between p-3 text-sm font-medium text-zinc-200">
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<svg class="h-4 w-4 text-zinc-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||||
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2h-1V9z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
How does the Gate work?
|
||||||
|
</span>
|
||||||
|
<svg class="h-4 w-4 text-zinc-500 transition-transform group-open:rotate-180" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||||
|
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</summary>
|
||||||
|
<div class="space-y-3 border-t border-zinc-900 p-4 text-sm text-zinc-300">
|
||||||
|
<ol class="space-y-3">
|
||||||
|
<li class="flex gap-3">
|
||||||
|
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-brand-500/15 text-xs font-semibold text-brand-300">1</span>
|
||||||
|
<p>
|
||||||
|
When you send Aunty Patience her invitation, she gets her own personal link.
|
||||||
|
That link only belongs to her.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li class="flex gap-3">
|
||||||
|
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-brand-500/15 text-xs font-semibold text-brand-300">2</span>
|
||||||
|
<p>
|
||||||
|
The first time Aunty opens her link, the Gate quietly remembers a few details:
|
||||||
|
her phone or browser, the general area she connected from, and the network shape.
|
||||||
|
That becomes her "I'm me" signature.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li class="flex gap-3">
|
||||||
|
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-brand-500/15 text-xs font-semibold text-brand-300">3</span>
|
||||||
|
<p>
|
||||||
|
Every time someone clicks Aunty's link after that — including Aunty herself —
|
||||||
|
the Gate compares them to her signature. Same phone, similar location?
|
||||||
|
They sail through. No signature yet? They become Aunty's signature on the first visit.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li class="flex gap-3">
|
||||||
|
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-brand-500/15 text-xs font-semibold text-brand-300">4</span>
|
||||||
|
<p>
|
||||||
|
If someone with a totally different phone on a totally different network tries to
|
||||||
|
use Aunty's link — like a friend Aunty forwarded it to — the Gate notices. Depending
|
||||||
|
on your strictness setting it'll either flag them for your review, or refuse them
|
||||||
|
outright.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p class="rounded-md border border-zinc-800 bg-zinc-900/60 p-3 text-xs text-zinc-400">
|
||||||
|
<strong class="text-zinc-300">Worth knowing:</strong> guests can switch from mobile data
|
||||||
|
to Wi-Fi or change rooms without being flagged — the Gate only cares about
|
||||||
|
<em>meaningful</em> differences from the original visit, not normal day-to-day variation.
|
||||||
|
If a real guest does ever get flagged (it happens), you can clear them with one click
|
||||||
|
in <em>Recent reviews</em> below.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
<p v-if="error" class="mb-3 text-sm text-red-400">{{ error }}</p>
|
<p v-if="error" class="mb-3 text-sm text-red-400">{{ error }}</p>
|
||||||
<p v-if="loading" class="text-sm text-zinc-500">Loading gate…</p>
|
<p v-if="loading" class="text-sm text-zinc-500">Loading gate…</p>
|
||||||
@@ -262,9 +357,9 @@ function verdictLabel(v: string) {
|
|||||||
<div v-else class="space-y-8">
|
<div v-else class="space-y-8">
|
||||||
<!-- Strictness presets -->
|
<!-- Strictness presets -->
|
||||||
<div>
|
<div>
|
||||||
<h3 class="mb-1 text-sm font-semibold text-zinc-100">How strict should the gate be?</h3>
|
<h3 class="mb-1 text-sm font-semibold text-zinc-100">How strict should the Gate be?</h3>
|
||||||
<p class="mb-3 text-xs text-zinc-500">
|
<p class="mb-3 text-xs text-zinc-500">
|
||||||
Pick the option that fits the kind of event you're hosting.
|
Pick the option that fits the kind of event you're hosting. You can change it any time.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||||
@@ -349,12 +444,32 @@ function verdictLabel(v: string) {
|
|||||||
|
|
||||||
<!-- Trusted networks -->
|
<!-- Trusted networks -->
|
||||||
<div>
|
<div>
|
||||||
<h3 class="mb-1 text-sm font-semibold text-zinc-100">Trusted networks</h3>
|
<div class="mb-2 flex items-center gap-2">
|
||||||
<p class="mb-3 text-xs text-zinc-500">
|
<h3 class="text-sm font-semibold text-zinc-100">Trusted networks</h3>
|
||||||
Guests arriving from one of these networks skip the gate entirely.
|
<span class="rounded-full border border-zinc-700 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wider text-zinc-500">
|
||||||
Useful for your home Wi-Fi, the venue's guest network, or your office —
|
Optional · most hosts don't need this
|
||||||
places where the people connecting are already known to be your guests.
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Direct address to the misconception the user raised. "Does that
|
||||||
|
mean everyone outside is treated as fraud?" — explicit "no".
|
||||||
|
This sits *above* the inputs so a host can't miss it. -->
|
||||||
|
<div class="mb-3 rounded-md border border-zinc-800 bg-zinc-950 p-3 text-xs text-zinc-400">
|
||||||
|
<p class="mb-1 text-zinc-300">
|
||||||
|
<strong>What this is and isn't:</strong>
|
||||||
</p>
|
</p>
|
||||||
|
<p class="mb-2">
|
||||||
|
Adding a network here tells the Gate "always wave through clicks coming from
|
||||||
|
this Wi-Fi — they're already with me." It's useful when you're hosting at home
|
||||||
|
or the office and your guests will connect through your network.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong class="text-zinc-300">It doesn't change how anyone else is treated.</strong>
|
||||||
|
Guests connecting from their own homes, mobile data, or anywhere else still get
|
||||||
|
the regular check — they're not suspected of anything by default. The Gate works
|
||||||
|
perfectly well with this list empty.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="canEdit" class="mb-3 flex flex-wrap items-center gap-2">
|
<div v-if="canEdit" class="mb-3 flex flex-wrap items-center gap-2">
|
||||||
<button
|
<button
|
||||||
@@ -366,13 +481,13 @@ function verdictLabel(v: string) {
|
|||||||
<svg class="mr-1 inline-block h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
<svg class="mr-1 inline-block h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||||
<path d="M10 18a8 8 0 100-16 8 8 0 000 16zm0-2a6 6 0 100-12 6 6 0 000 12zm0-3a3 3 0 100-6 3 3 0 000 6z" />
|
<path d="M10 18a8 8 0 100-16 8 8 0 000 16zm0-2a6 6 0 100-12 6 6 0 000 12zm0-3a3 3 0 100-6 3 3 0 000 6z" />
|
||||||
</svg>
|
</svg>
|
||||||
{{ detectingIP ? 'Detecting…' : 'Use my current network' }}
|
{{ detectingIP ? 'Detecting…' : 'Trust the network I’m on right now' }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="text-sm text-zinc-400 hover:text-zinc-200"
|
class="text-sm text-zinc-400 hover:text-zinc-200"
|
||||||
@click="showAddNetwork = !showAddNetwork"
|
@click="showAddNetwork = !showAddNetwork"
|
||||||
>or add manually</button>
|
>or add one manually</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Manual add form — only shown when the host opens it. Inputs
|
<!-- Manual add form — only shown when the host opens it. Inputs
|
||||||
@@ -431,7 +546,9 @@ function verdictLabel(v: string) {
|
|||||||
>Remove</button>
|
>Remove</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p v-else class="text-sm text-zinc-500">No trusted networks yet.</p>
|
<p v-else class="text-sm text-zinc-500">
|
||||||
|
No trusted networks — and that's fine. The Gate is doing its job.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Recent gate decisions -->
|
<!-- Recent gate decisions -->
|
||||||
|
|||||||
Reference in New Issue
Block a user