{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "trust-1",
  "title": "Trust 1: security and trust center",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A trust center: certification cards with dates, the security controls behind them, documents where audit reports ask for a request under a confidentiality agreement, a subprocessor table with change alerts, and a disclosure contact.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/blur-fade.json",
    "https://ui.ballmac.com/r/button.json",
    "https://ui.ballmac.com/r/checkbox.json",
    "https://ui.ballmac.com/r/cover-art.json",
    "https://ui.ballmac.com/r/dialog.json",
    "https://ui.ballmac.com/r/input.json",
    "https://ui.ballmac.com/r/skeleton.json",
    "https://ui.ballmac.com/r/spotlight-card.json",
    "https://ui.ballmac.com/r/state-view.json",
    "https://ui.ballmac.com/r/return-focus.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/trust-1/trust-1.tsx",
      "content": "// Ballmac UI: Trust 1. https://ui.ballmac.com/blocks/trust-1\n\"use client\"\n\nimport * as React from \"react\"\nimport { Bell, Bug, CalendarCheck, CircleAlert, CircleCheck, Clock, Download, FileText, Loader2, Lock, MailCheck, Search, Server, ShieldCheck } from \"lucide-react\"\n\nimport { BlurFade, BlurFadeGroup } from \"@/components/ballmac/blur-fade\"\nimport { Button } from \"@/components/ballmac/button\"\nimport { Checkbox } from \"@/components/ballmac/checkbox\"\nimport { CoverArt } from \"@/components/ballmac/cover-art\"\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"@/components/ballmac/dialog\"\nimport { Input } from \"@/components/ballmac/input\"\nimport { Skeleton } from \"@/components/ballmac/skeleton\"\nimport { useReturnFocus } from \"@/lib/ballmac/return-focus\"\nimport { SpotlightCard } from \"@/components/ballmac/spotlight-card\"\nimport { frameProps, StateView, type StateProps } from \"@/components/ballmac/state-view\"\nimport { cn } from \"@/lib/utils\"\n\ntype Trust1Certification = {\n  id: string\n  /** The standard's short name: \"SOC 2 Type II\". */\n  name: string\n  status: \"certified\" | \"in-progress\" | \"aligned\"\n  /** Who audited it or what it covers. */\n  detail: string\n  /** When it is valid until, or when the next step happens. */\n  valid?: string\n}\n\ntype Trust1ControlGroup = {\n  id: string\n  title: string\n  icon?: React.ReactNode\n  controls: { text: string; status?: \"done\" | \"progress\" }[]\n}\n\ntype Trust1Document = {\n  id: string\n  name: string\n  description?: string\n  /** A public document downloads straight away; a gated one asks for a request first. */\n  access: \"public\" | \"gated\"\n  /** \"PDF · 2.4 MB\". */\n  format: string\n  /** Already written for people (\"Updated June 2026\"). */\n  updated: string\n  href?: string\n}\n\ntype Trust1Subprocessor = { name: string; purpose: string; location: string; data: string }\n\ntype Trust1Props = Omit<React.ComponentProps<\"section\">, \"title\"> &\n  Pick<StateProps, \"state\" | \"onRetry\" | \"errorState\"> & {\n    title?: string\n    description?: string\n    /** The line in the status pill. */\n    status?: { label: string; ok?: boolean; updated?: string }\n    certifications?: Trust1Certification[]\n    groups?: Trust1ControlGroup[]\n    documents?: Trust1Document[]\n    subprocessors?: Trust1Subprocessor[]\n    /** Where vulnerability reports go. */\n    securityEmail?: string\n    /** Your company's name, used in the confidentiality line. */\n    companyName?: string\n    /** The id of a gated document whose request dialog is open at first. */\n    defaultRequestId?: string\n    /** Called when a public document is downloaded. */\n    onDownload?: (doc: Trust1Document) => void\n    /** Called with a request for a gated document. Resolve to confirm; throw to show the reason. Defaults to a short simulated request. */\n    onRequestAccess?: (doc: Trust1Document, details: { email: string; company: string }) => Promise<void>\n    /** Called with an email to notify when the list of subprocessors changes. Resolve to confirm; throw to show the reason. */\n    onSubscribe?: (email: string) => Promise<void>\n  }\n\nconst sampleCerts: Trust1Certification[] = [\n  { id: \"soc2\", name: \"SOC 2 Type II\", status: \"certified\", detail: \"Audited by Halden & Rowe, independent auditors\", valid: \"Valid until 14 Jun 2027\" },\n  { id: \"iso\", name: \"ISO 27001\", status: \"certified\", detail: \"Information security management system\", valid: \"Valid until 2 Nov 2027\" },\n  { id: \"gdpr\", name: \"GDPR\", status: \"aligned\", detail: \"Data processing agreement and EU data residency\", valid: \"DPA available below\" },\n  { id: \"hipaa\", name: \"HIPAA\", status: \"in-progress\", detail: \"Controls mapped; independent assessment scheduled\", valid: \"Assessment in Q1 2027\" },\n]\n\nconst sampleGroups: Trust1ControlGroup[] = [\n  {\n    id: \"data\",\n    title: \"Data protection\",\n    icon: <Lock />,\n    controls: [\n      { text: \"Encrypted at rest with AES-256\", status: \"done\" },\n      { text: \"TLS 1.3 for all traffic, in transit and between services\", status: \"done\" },\n      { text: \"Each customer's data is isolated in its own workspace\", status: \"done\" },\n      { text: \"Daily encrypted backups, restored and tested every quarter\", status: \"done\" },\n    ],\n  },\n  {\n    id: \"access\",\n    title: \"Access control\",\n    icon: <ShieldCheck />,\n    controls: [\n      { text: \"Single sign-on and SCIM for your team\", status: \"done\" },\n      { text: \"Two-step sign-in required for every employee\", status: \"done\" },\n      { text: \"Access reviewed every quarter, with least privilege\", status: \"done\" },\n      { text: \"Every admin action recorded in an audit log\", status: \"done\" },\n    ],\n  },\n  {\n    id: \"infra\",\n    title: \"Infrastructure\",\n    icon: <Server />,\n    controls: [\n      { text: \"Hosted in ISO-certified data centers\", status: \"done\" },\n      { text: \"EU and US regions, chosen when you sign up\", status: \"done\" },\n      { text: \"99.95% uptime over the last 12 months\", status: \"done\" },\n      { text: \"Disaster recovery rehearsed once a year\", status: \"done\" },\n    ],\n  },\n  {\n    id: \"app\",\n    title: \"Application security\",\n    icon: <Bug />,\n    controls: [\n      { text: \"Independent penetration test every year\", status: \"done\" },\n      { text: \"Dependency and secret scanning on every commit\", status: \"done\" },\n      { text: \"Security training for every employee\", status: \"done\" },\n      { text: \"Bug bounty with cash rewards\", status: \"progress\" },\n    ],\n  },\n]\n\nconst sampleDocs: Trust1Document[] = [\n  { id: \"wp\", name: \"Security whitepaper\", description: \"How the product is built, run and protected.\", access: \"public\", format: \"PDF · 2.4 MB\", updated: \"Updated June 2026\" },\n  { id: \"dpa\", name: \"Data processing agreement\", description: \"The standard DPA, ready to sign.\", access: \"public\", format: \"PDF · 480 KB\", updated: \"Updated March 2026\" },\n  { id: \"soc2\", name: \"SOC 2 Type II report\", description: \"The full audit report, under a confidentiality agreement.\", access: \"gated\", format: \"PDF · 6.1 MB\", updated: \"Updated June 2026\" },\n  { id: \"pen\", name: \"Penetration test summary\", description: \"Findings and fixes from the latest independent test.\", access: \"gated\", format: \"PDF · 1.2 MB\", updated: \"Updated May 2026\" },\n]\n\nconst sampleSubs: Trust1Subprocessor[] = [\n  { name: \"Cloud hosting\", purpose: \"Runs the product and stores data\", location: \"Frankfurt, Germany\", data: \"All customer data\" },\n  { name: \"Email delivery\", purpose: \"Sends sign-in and notification email\", location: \"Dublin, Ireland\", data: \"Names and email addresses\" },\n  { name: \"Payments\", purpose: \"Processes card payments\", location: \"United States\", data: \"Billing details\" },\n  { name: \"Error monitoring\", purpose: \"Finds and fixes crashes\", location: \"Frankfurt, Germany\", data: \"Technical diagnostics, never content\" },\n  { name: \"Support desk\", purpose: \"Answers customer questions\", location: \"Dublin, Ireland\", data: \"Support conversations\" },\n  { name: \"Product analytics\", purpose: \"Shows which features are used\", location: \"Frankfurt, Germany\", data: \"Usage events, never content\" },\n]\n\nconst emailOk = (v: string) => /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v.trim())\nconst statusMeta = {\n  certified: { label: \"Certified\", icon: CircleCheck, tone: \"bg-chart-2/15 text-foreground border-chart-2/30\" },\n  aligned: { label: \"Compliant\", icon: CircleCheck, tone: \"bg-chart-1/15 text-foreground border-chart-1/30\" },\n  \"in-progress\": { label: \"In progress\", icon: Clock, tone: \"bg-chart-3/20 text-foreground border-chart-3/40\" },\n} as const\n\nfunction TrustSkeleton() {\n  return (\n    <div className=\"grid gap-12\" aria-hidden=\"true\">\n      <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        {[0, 1, 2, 3].map((i) => (\n          <Skeleton key={i} className=\"h-48 rounded-2xl\" />\n        ))}\n      </div>\n      <div className=\"grid gap-4 md:grid-cols-2\">\n        {[0, 1, 2, 3].map((i) => (\n          <Skeleton key={i} className=\"h-52 rounded-2xl\" />\n        ))}\n      </div>\n    </div>\n  )\n}\n\n/**\n * A trust center: the standards you meet with dates, the security controls behind them, documents (public ones download, sensitive ones\n * ask for a request with a confidentiality agreement), the subprocessors that touch customer data with a way to be told of changes, and\n * where to report a vulnerability.\n */\nfunction Trust1({\n  title = \"Trust center\",\n  description = \"How we protect your data, who helps us do it, and the proof. Everything on this page is kept current.\",\n  status = { label: \"All systems operational\", ok: true, updated: \"Checked 2 minutes ago\" },\n  certifications = sampleCerts,\n  groups = sampleGroups,\n  documents = sampleDocs,\n  subprocessors = sampleSubs,\n  securityEmail = \"security@acme.com\",\n  companyName = \"Acme\",\n  defaultRequestId,\n  onDownload,\n  onRequestAccess,\n  onSubscribe,\n  state = \"ready\",\n  onRetry,\n  errorState,\n  className,\n  ...props\n}: Trust1Props) {\n  const id = React.useId()\n  const [query, setQuery] = React.useState(\"\")\n  const [request, setRequest] = React.useState<Trust1Document | null>(() => documents.find((d) => d.id === defaultRequestId && d.access === \"gated\") ?? null)\n  const returnFocus = useReturnFocus(!!request)\n  const [form, setForm] = React.useState({ email: \"\", company: \"\", agree: false })\n  const [phase, setPhase] = React.useState<\"idle\" | \"sending\" | \"error\" | \"done\">(\"idle\")\n  const [message, setMessage] = React.useState<string>()\n  const [touched, setTouched] = React.useState(false)\n  const [subEmail, setSubEmail] = React.useState(\"\")\n  const [subPhase, setSubPhase] = React.useState<\"idle\" | \"sending\" | \"error\" | \"done\">(\"idle\")\n  const [subMessage, setSubMessage] = React.useState<string>()\n\n  const q = query.trim().toLowerCase()\n  const subs = subprocessors.filter((s) => !q || `${s.name} ${s.purpose} ${s.location} ${s.data}`.toLowerCase().includes(q))\n\n  const emailError = touched && !emailOk(form.email) ? \"Enter a work email, like name@company.com.\" : undefined\n  const companyError = touched && !form.company.trim() ? \"Tell us which company this is for.\" : undefined\n  const agreeError = touched && !form.agree ? \"Agree to keep it confidential to continue.\" : undefined\n\n  async function submit(e: React.FormEvent) {\n    e.preventDefault()\n    setTouched(true)\n    if (!request || !emailOk(form.email) || !form.company.trim() || !form.agree) return\n    setPhase(\"sending\")\n    setMessage(undefined)\n    try {\n      if (onRequestAccess) await onRequestAccess(request, { email: form.email.trim(), company: form.company.trim() })\n      else await new Promise((r) => setTimeout(r, 900))\n      setPhase(\"done\")\n    } catch (err) {\n      setPhase(\"error\")\n      setMessage(err instanceof Error && err.message ? err.message : \"The request could not be sent. Try again in a moment.\")\n    }\n  }\n\n  async function subscribe(e: React.FormEvent) {\n    e.preventDefault()\n    if (!emailOk(subEmail)) {\n      setSubPhase(\"error\")\n      setSubMessage(\"Enter a valid email address.\")\n      return\n    }\n    setSubPhase(\"sending\")\n    try {\n      if (onSubscribe) await onSubscribe(subEmail.trim())\n      else await new Promise((r) => setTimeout(r, 700))\n      setSubPhase(\"done\")\n    } catch (err) {\n      setSubPhase(\"error\")\n      setSubMessage(err instanceof Error && err.message ? err.message : \"We could not sign you up. Try again.\")\n    }\n  }\n\n  const content = (\n    <div className=\"grid grid-cols-1 gap-20\">\n      <section aria-labelledby={`${id}-certs`}>\n        <h2 id={`${id}-certs`} className=\"mb-6 text-2xl font-semibold tracking-[-0.03em]\">\n          Standards we meet\n        </h2>\n        <BlurFadeGroup stagger={0.06} className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4 [&>*]:h-full [&>*>*]:h-full\" item={{ direction: \"up\", offset: 12, blur: 6 }}>\n          {certifications.map((c) => {\n            const S = statusMeta[c.status]\n            return (\n              <SpotlightCard key={c.id} size={280} className=\"flex flex-col gap-4 rounded-2xl p-6 transition-shadow duration-200 hover:shadow-md\">\n                <div className=\"flex items-start justify-between gap-2\">\n                  <span aria-hidden=\"true\" className=\"bg-muted flex size-10 items-center justify-center rounded-xl [&_svg]:size-5\">\n                    <ShieldCheck />\n                  </span>\n                  <span className={cn(\"inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium\", S.tone)}>\n                    <S.icon aria-hidden=\"true\" className=\"size-3.5\" />\n                    {S.label}\n                  </span>\n                </div>\n                <div className=\"flex-1\">\n                  <h3 className=\"text-xl font-semibold tracking-tight\">{c.name}</h3>\n                  <p className=\"text-muted-foreground mt-1.5 text-sm text-pretty\">{c.detail}</p>\n                </div>\n                {c.valid ? (\n                  <p className=\"text-muted-foreground flex items-center gap-2 border-t pt-4 text-xs\">\n                    <CalendarCheck aria-hidden=\"true\" className=\"size-3.5 shrink-0\" />\n                    {c.valid}\n                  </p>\n                ) : null}\n              </SpotlightCard>\n            )\n          })}\n        </BlurFadeGroup>\n      </section>\n\n      <section aria-labelledby={`${id}-controls`}>\n        <h2 id={`${id}-controls`} className=\"mb-2 text-2xl font-semibold tracking-[-0.03em]\">\n          How we protect your data\n        </h2>\n        <p className=\"text-muted-foreground mb-6 max-w-xl text-sm text-pretty\">The controls behind the certificates, written as plainly as we can.</p>\n        <div className=\"grid gap-4 md:grid-cols-2\">\n          {groups.map((g, gi) => (\n            <BlurFade key={g.id} direction=\"up\" offset={12} delay={0.05 * gi}>\n              <div className=\"bg-card h-full rounded-2xl border p-6\">\n                <div className=\"flex items-center gap-3\">\n                  <span aria-hidden=\"true\" className={cn(\"flex size-9 items-center justify-center rounded-lg [&_svg]:size-4\", [\"bg-chart-1/20\", \"bg-chart-2/20\", \"bg-chart-4/20\", \"bg-chart-3/25\"][gi % 4])}>\n                    {g.icon ?? <Lock />}\n                  </span>\n                  <h3 className=\"flex-1 text-base font-semibold tracking-tight\">{g.title}</h3>\n                  <span className=\"text-muted-foreground text-xs tabular-nums\">{g.controls.length} controls</span>\n                </div>\n                <ul className=\"mt-5 grid gap-3\">\n                  {g.controls.map((c) => (\n                    <li key={c.text} className=\"flex items-start gap-3 text-sm\">\n                      {c.status === \"progress\" ? <Clock aria-hidden=\"true\" className=\"text-chart-3 mt-0.5 size-4 shrink-0\" /> : <CircleCheck aria-hidden=\"true\" className=\"text-chart-2 mt-0.5 size-4 shrink-0\" />}\n                      <span className=\"text-pretty\">\n                        {c.text}\n                        {c.status === \"progress\" ? <span className=\"text-muted-foreground\"> (in progress)</span> : null}\n                      </span>\n                    </li>\n                  ))}\n                </ul>\n              </div>\n            </BlurFade>\n          ))}\n        </div>\n      </section>\n\n      <section aria-labelledby={`${id}-docs`}>\n        <h2 id={`${id}-docs`} className=\"mb-2 text-2xl font-semibold tracking-[-0.03em]\">\n          Documents\n        </h2>\n        <p className=\"text-muted-foreground mb-6 max-w-xl text-sm text-pretty\">Public documents download straight away. Audit reports are shared under a confidentiality agreement.</p>\n        <ul className=\"bg-card divide-y overflow-hidden rounded-2xl border\">\n          {documents.map((d) => (\n            <li key={d.id} className=\"flex flex-wrap items-center gap-x-5 gap-y-3 p-5\">\n              <span aria-hidden=\"true\" className=\"bg-muted flex size-11 shrink-0 items-center justify-center rounded-xl [&_svg]:size-5\">\n                {d.access === \"gated\" ? <Lock /> : <FileText />}\n              </span>\n              <div className=\"min-w-0 flex-1 basis-64\">\n                <p className=\"flex flex-wrap items-center gap-2 text-sm font-medium\">\n                  {d.name}\n                  {d.access === \"gated\" ? <span className=\"bg-muted text-muted-foreground rounded-full px-2 py-0.5 text-[11px] font-normal\">Request required</span> : null}\n                </p>\n                {d.description ? <p className=\"text-muted-foreground mt-0.5 text-xs text-pretty\">{d.description}</p> : null}\n                <p className=\"text-muted-foreground mt-1 text-xs tabular-nums\">\n                  {d.format} · {d.updated}\n                </p>\n              </div>\n              {d.access === \"public\" ? (\n                d.href ? (\n                  <a href={d.href} onClick={() => onDownload?.(d)} className=\"hover:bg-accent focus-visible:ring-ring/50 inline-flex h-9 items-center gap-2 rounded-md border px-3.5 text-sm font-medium outline-none focus-visible:ring-[3px]\">\n                    <Download aria-hidden=\"true\" className=\"size-4\" />\n                    Download{\" \"}\n                    <span className=\"sr-only\">{d.name}</span>\n                  </a>\n                ) : (\n                  <Button variant=\"outline\" onClick={() => onDownload?.(d)}>\n                    <Download aria-hidden=\"true\" />\n                    Download{\" \"}\n                    <span className=\"sr-only\">{d.name}</span>\n                  </Button>\n                )\n              ) : (\n                <Button\n                  onClick={() => {\n                    setRequest(d)\n                    setPhase(\"idle\")\n                    setTouched(false)\n                    setMessage(undefined)\n                  }}\n                >\n                  Request access\n                  {\" \"}<span className=\"sr-only\">to {d.name}</span>\n                </Button>\n              )}\n            </li>\n          ))}\n        </ul>\n      </section>\n\n      <section aria-labelledby={`${id}-subs`}>\n        <div className=\"mb-6 flex flex-wrap items-end justify-between gap-x-8 gap-y-4\">\n          <div className=\"max-w-xl\">\n            <h2 id={`${id}-subs`} className=\"text-2xl font-semibold tracking-[-0.03em]\">\n              Subprocessors\n            </h2>\n            <p className=\"text-muted-foreground mt-2 text-sm text-pretty\">The companies that handle customer data for us, what they do, and where. We tell you 30 days before we add one.</p>\n          </div>\n          <form onSubmit={subscribe} noValidate className=\"grid w-full max-w-sm gap-1.5\">\n            <label htmlFor={`${id}-sub`} className=\"text-xs font-medium\">\n              Get an email when this list changes\n            </label>\n            {subPhase === \"done\" ? (\n              <p role=\"status\" className=\"bg-chart-2/10 border-chart-2/30 flex items-center gap-2 rounded-md border px-3 py-2 text-sm\">\n                <MailCheck aria-hidden=\"true\" className=\"size-4 shrink-0\" />\n                You are on the list. We will email {subEmail}.\n              </p>\n            ) : (\n              <>\n                <div className=\"flex gap-2\">\n                  <Input\n                    id={`${id}-sub`}\n                    type=\"email\"\n                    autoComplete=\"email\"\n                    placeholder=\"you@company.com\"\n                    value={subEmail}\n                    onChange={(e) => {\n                      setSubEmail(e.target.value)\n                      if (subPhase === \"error\") setSubPhase(\"idle\")\n                    }}\n                    aria-invalid={subPhase === \"error\" || undefined}\n                    aria-describedby={subPhase === \"error\" ? `${id}-sub-error` : undefined}\n                  />\n                  <Button type=\"submit\" disabled={subPhase === \"sending\"}>\n                    {subPhase === \"sending\" ? <Loader2 aria-hidden=\"true\" className=\"animate-spin motion-reduce:animate-none\" /> : <Bell aria-hidden=\"true\" />}\n                    {subPhase === \"sending\" ? \"Signing up…\" : \"Notify me\"}\n                  </Button>\n                </div>\n                {subPhase === \"error\" ? (\n                  <p id={`${id}-sub-error`} role=\"alert\" className=\"text-destructive text-xs\">\n                    {subMessage}\n                  </p>\n                ) : null}\n              </>\n            )}\n          </form>\n        </div>\n        <div className=\"bg-card min-w-0 overflow-hidden rounded-2xl border\">\n          <div className=\"flex items-center gap-2 border-b px-4 py-2.5\">\n            <Search aria-hidden=\"true\" className=\"text-muted-foreground size-4\" />\n            <input type=\"search\" aria-label=\"Filter subprocessors\" placeholder=\"Filter by name, purpose or place\" value={query} onChange={(e) => setQuery(e.target.value)} className=\"placeholder:text-muted-foreground min-w-0 flex-1 bg-transparent text-sm outline-none\" />\n            <span className=\"text-muted-foreground text-xs tabular-nums\">{subs.length} of {subprocessors.length}</span>\n          </div>\n          <div tabIndex={0} role=\"region\" aria-label=\"Subprocessors table\" className=\"focus-visible:ring-ring/50 overflow-x-auto outline-none focus-visible:ring-[3px] focus-visible:ring-inset\">\n            <table className=\"w-full min-w-[40rem] text-sm\">\n              <thead>\n                <tr className=\"text-muted-foreground border-b text-xs\">\n                  <th scope=\"col\" className=\"px-4 py-2.5 text-start font-medium\">Name</th>\n                  <th scope=\"col\" className=\"px-4 py-2.5 text-start font-medium\">What they do</th>\n                  <th scope=\"col\" className=\"px-4 py-2.5 text-start font-medium\">Where</th>\n                  <th scope=\"col\" className=\"px-4 py-2.5 text-start font-medium\">Data they handle</th>\n                </tr>\n              </thead>\n              <tbody>\n                {subs.map((s) => (\n                  <tr key={s.name} className=\"hover:bg-accent/30 border-b last:border-0\">\n                    <th scope=\"row\" className=\"px-4 py-3 text-start font-medium\">{s.name}</th>\n                    <td className=\"text-muted-foreground px-4 py-3\">{s.purpose}</td>\n                    <td className=\"px-4 py-3 whitespace-nowrap\">{s.location}</td>\n                    <td className=\"text-muted-foreground px-4 py-3\">{s.data}</td>\n                  </tr>\n                ))}\n                {!subs.length ? (\n                  <tr>\n                    <td colSpan={4} className=\"text-muted-foreground px-4 py-10 text-center\">\n                      No subprocessors match “{query}”.\n                    </td>\n                  </tr>\n                ) : null}\n              </tbody>\n            </table>\n          </div>\n        </div>\n      </section>\n\n    </div>\n  )\n\n  const report = (\n      <BlurFade direction=\"up\" offset={14}>\n        <section aria-labelledby={`${id}-report`} className=\"bg-card relative isolate overflow-hidden rounded-3xl border p-8 sm:p-12\">\n          <CoverArt seed=\"disclosure\" variant=\"rings\" className=\"absolute inset-y-0 end-0 -z-10 w-2/3 opacity-60 [mask-image:linear-gradient(to_left,black,transparent)] rtl:[mask-image:linear-gradient(to_right,black,transparent)]\" />\n          <div className=\"max-w-xl\">\n            <span aria-hidden=\"true\" className=\"bg-muted flex size-11 items-center justify-center rounded-xl\">\n              <Bug className=\"size-5\" />\n            </span>\n            <h2 id={`${id}-report`} className=\"mt-5 text-2xl font-semibold tracking-[-0.03em] sm:text-3xl\">\n              Found a security issue?\n            </h2>\n            <p className=\"text-muted-foreground mt-2 text-sm leading-relaxed text-pretty\">Please tell us before you tell anyone else. We answer within one working day, we do not take legal action against good-faith research, and we credit you if you like.</p>\n            <a href={`mailto:${securityEmail}`} className=\"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring/50 mt-6 inline-flex h-10 items-center rounded-md px-5 text-sm font-medium outline-none focus-visible:ring-[3px]\">\n              {securityEmail}\n            </a>\n          </div>\n        </section>\n      </BlurFade>\n  )\n\n  return (\n    <section data-slot=\"trust-1\" data-state={state} aria-labelledby={`${id}-title`} className={cn(\"mx-auto w-full max-w-6xl px-4 py-12 sm:px-6\", className)} {...frameProps(props)}>\n      <BlurFade direction=\"up\" offset={12} duration={0.55}>\n        <header className=\"bg-card relative isolate mb-14 overflow-hidden rounded-3xl border p-8 sm:p-12\">\n          <CoverArt seed=\"trust-center\" variant=\"mesh\" grid className=\"absolute inset-0 -z-10 opacity-50\" />\n          <div aria-hidden=\"true\" className=\"from-card via-card/80 absolute inset-0 -z-10 bg-gradient-to-r to-transparent\" />\n          <p role=\"status\" className=\"bg-background/80 mb-6 inline-flex items-center gap-2 rounded-full border py-1 ps-2.5 pe-3 text-xs backdrop-blur\">\n            <span aria-hidden=\"true\" className={cn(\"size-2 rounded-full\", status.ok === false ? \"bg-destructive\" : \"bg-chart-2\")} />\n            <span className=\"font-medium\">{status.label}</span>\n            {status.updated ? <span className=\"text-muted-foreground\">· {status.updated}</span> : null}\n          </p>\n          <h1 id={`${id}-title`} className=\"flex items-center gap-3 text-4xl font-semibold tracking-[-0.04em] text-balance sm:text-6xl\">\n            {title}\n          </h1>\n          <p className=\"text-muted-foreground mt-4 max-w-xl text-base leading-relaxed text-pretty\">{description}</p>\n        </header>\n      </BlurFade>\n\n      <StateView state={state === \"loading\" || state === \"error\" ? state : \"ready\"} onRetry={onRetry} label=\"Loading the trust center\" loading={<TrustSkeleton />} error={{ title: errorState?.title ?? \"The trust center could not be loaded\", description: errorState?.description ?? \"Check your connection and try again. You can still reach us at the address below.\", ...errorState }}>\n        {content}\n      </StateView>\n      <div className=\"mt-20\">{report}</div>\n\n      <Dialog open={!!request} onOpenChange={(o) => !o && setRequest(null)}>\n        <DialogContent {...returnFocus}>\n          {phase === \"done\" ? (\n            <div className=\"grid justify-items-center gap-3 py-4 text-center\">\n              <span aria-hidden=\"true\" className=\"bg-chart-2/15 flex size-12 items-center justify-center rounded-full\">\n                <MailCheck className=\"size-6\" />\n              </span>\n              <DialogTitle>Request sent</DialogTitle>\n              <DialogDescription className=\"max-w-xs text-pretty\">\n                We will email {form.email} within one working day with a link to {request?.name}.\n              </DialogDescription>\n              <Button className=\"mt-2\" onClick={() => setRequest(null)}>\n                Done\n              </Button>\n            </div>\n          ) : (\n            <form onSubmit={submit} noValidate className=\"grid gap-5\">\n              <DialogHeader>\n                <DialogTitle>Request {request?.name}</DialogTitle>\n                <DialogDescription>We share this under a confidentiality agreement. It takes a minute.</DialogDescription>\n              </DialogHeader>\n              <div className=\"grid gap-1.5\">\n                <label htmlFor={`${id}-email`} className=\"text-sm font-medium\">\n                  Work email\n                </label>\n                <Input id={`${id}-email`} type=\"email\" autoComplete=\"email\" value={form.email} onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))} aria-invalid={!!emailError || undefined} aria-describedby={emailError ? `${id}-email-error` : undefined} />\n                {emailError ? <p id={`${id}-email-error`} role=\"alert\" className=\"text-destructive text-xs\">{emailError}</p> : null}\n              </div>\n              <div className=\"grid gap-1.5\">\n                <label htmlFor={`${id}-company`} className=\"text-sm font-medium\">\n                  Company\n                </label>\n                <Input id={`${id}-company`} autoComplete=\"organization\" value={form.company} onChange={(e) => setForm((f) => ({ ...f, company: e.target.value }))} aria-invalid={!!companyError || undefined} aria-describedby={companyError ? `${id}-company-error` : undefined} />\n                {companyError ? <p id={`${id}-company-error`} role=\"alert\" className=\"text-destructive text-xs\">{companyError}</p> : null}\n              </div>\n              <div className=\"grid gap-1.5\">\n                <div className=\"flex items-start gap-3\">\n                  <Checkbox id={`${id}-nda`} checked={form.agree} onCheckedChange={(c) => setForm((f) => ({ ...f, agree: c === true }))} aria-invalid={!!agreeError || undefined} aria-describedby={agreeError ? `${id}-nda-error` : undefined} className=\"mt-0.5\" />\n                  <label htmlFor={`${id}-nda`} className=\"text-sm text-pretty\">\n                    I agree to keep this document confidential and to use it only to assess {companyName}.\n                  </label>\n                </div>\n                {agreeError ? <p id={`${id}-nda-error`} role=\"alert\" className=\"text-destructive text-xs\">{agreeError}</p> : null}\n              </div>\n              {phase === \"error\" ? (\n                <p role=\"alert\" className=\"border-destructive/30 bg-destructive/5 flex items-start gap-2 rounded-lg border px-3 py-2 text-sm\">\n                  <CircleAlert aria-hidden=\"true\" className=\"text-destructive mt-0.5 size-4 shrink-0\" />\n                  {message}\n                </p>\n              ) : null}\n              <div className=\"flex justify-end gap-2\">\n                <Button type=\"button\" variant=\"ghost\" onClick={() => setRequest(null)} disabled={phase === \"sending\"}>\n                  Cancel\n                </Button>\n                <Button type=\"submit\" disabled={phase === \"sending\"}>\n                  {phase === \"sending\" ? <Loader2 aria-hidden=\"true\" className=\"animate-spin motion-reduce:animate-none\" /> : null}\n                  {phase === \"sending\" ? \"Sending…\" : \"Send request\"}\n                </Button>\n              </div>\n            </form>\n          )}\n        </DialogContent>\n      </Dialog>\n    </section>\n  )\n}\n\nexport { Trust1, type Trust1Props, type Trust1Certification, type Trust1ControlGroup, type Trust1Document, type Trust1Subprocessor }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/trust-1/trust-1.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "trust",
      "security",
      "compliance",
      "soc 2",
      "iso 27001",
      "gdpr",
      "hipaa",
      "subprocessors",
      "privacy",
      "enterprise",
      "audit"
    ],
    "version": "1.0.0",
    "updated": "2026-10-18",
    "tests": true,
    "ai": {
      "summary": "A security and compliance page. <Trust1 status={{ label, ok, updated }} certifications={[{ id, name, status: 'certified' | 'aligned' | 'in-progress', detail, valid }]} groups={[{ id, title, icon, controls: [{ text, status: 'done' | 'progress' }] }]} documents={[{ id, name, description, access: 'public' | 'gated', format, updated, href }]} subprocessors={[{ name, purpose, location, data }]} securityEmail companyName onDownload onRequestAccess={async (doc, { email, company }) => …} onSubscribe={async (email) => …} />. Gated documents open a request dialog with validation and submitting, success and error states.",
      "whenToUse": [
        "The trust, security or compliance page a B2B buyer's security team reads",
        "Sharing SOC 2 and penetration test reports behind a request"
      ],
      "whenNotToUse": [
        "A privacy policy or terms (plain text pages)",
        "A system status page (use the relay template's status page or stats-pro-2)",
        "A cookie banner (use cookie-consent)"
      ],
      "composesWith": [
        "header-2",
        "footer-2",
        "about-1",
        "faq-1",
        "contact-1"
      ],
      "a11y": [
        {
          "keys": "Request access",
          "action": "Opens a dialog whose fields have labels; errors are announced and linked to their fields; focus returns to the button afterwards"
        },
        {
          "keys": "Status",
          "action": "Certified, Compliant and In progress are words with an icon, never color alone; controls in progress say so in the text"
        },
        {
          "keys": "Subprocessors table",
          "action": "A labelled, focusable scroll region on narrow screens, with row headers"
        }
      ],
      "customization": [
        "companyName for the confidentiality line",
        "defaultRequestId opens a request at first",
        "onRequestAccess should create the request in your CRM or send a secure link; never expose the file URL to the browser until it is approved"
      ]
    },
    "states": [
      {
        "id": "loading",
        "title": "Loading",
        "example": "trust-1-loading",
        "trigger": "state=\"loading\"",
        "note": "Skeleton certificate and control cards under the real header, announced as loading."
      },
      {
        "id": "error",
        "title": "Could not load",
        "example": "trust-1-error",
        "trigger": "state=\"error\"",
        "note": "An alert with Try again (onRetry). The header stays so the page never looks broken."
      }
    ],
    "examples": [
      "trust-1-demo",
      "trust-1-request",
      "trust-1-loading",
      "trust-1-error"
    ],
    "url": "https://ui.ballmac.com/components/trust-1"
  },
  "categories": [
    "blocks",
    "trust"
  ],
  "type": "registry:block"
}