[{"data":1,"prerenderedAt":6},["ShallowReactive",2],{"playground-demo-files:consent-gate":3},{"src\u002FApp.vue":4,"src\u002Fstyles.css":5},"\u003Cscript setup lang=\"ts\">\n  import { useForm, useWizard, gate } from 'attaform'\n  import { z } from 'zod'\n  import '.\u002Fstyles.css'\n\n  const consent = useForm({\n    schema: z.object({\n      accepted: z.literal(true, 'Accept the terms to continue'),\n    }),\n    defaultValues: { accepted: false },\n    key: 'gate-consent',\n  })\n\n  const shipping = useForm({\n    schema: z.object({\n      address: z.string().min(1, 'Address is required'),\n      city: z.string().min(1, 'City is required'),\n    }),\n    key: 'gate-shipping',\n  })\n\n  const payment = useForm({\n    schema: z.object({\n      cardNumber: z.string().min(12, 'Enter a card number'),\n    }),\n    key: 'gate-payment',\n  })\n\n  const wizard = useWizard({\n    steps: [gate(consent), shipping, payment],\n  })\n\n  const onFinish = wizard.handleSubmit(async (ctx) => {\n    await new Promise((resolve) => setTimeout(resolve, 400))\n    toast.success('Order confirmed, thanks!', { description: ctx.values })\n  })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"demo\">\n    \u003Col class=\"rail\">\n      \u003Cli\n        v-for=\"(step, i) in wizard.steps\"\n        :key=\"step.key\"\n        :class=\"{\n          done: wizard.statuses[step.key]?.valid === true && wizard.currentStep !== step.key,\n          current: wizard.currentStep === step.key,\n          locked: wizard.statuses[step.key]?.locked === true,\n        }\"\n      >\n        \u003Cbutton\n          type=\"button\"\n          class=\"step-button\"\n          :disabled=\"wizard.statuses[step.key]?.locked === true\"\n          @click=\"wizard.goTo(step.key)\"\n        >\n          \u003Cspan class=\"step-num\">\n            \u003Ctemplate v-if=\"wizard.statuses[step.key]?.locked === true\">🔒\u003C\u002Ftemplate>\n            \u003Ctemplate v-else>{{ i + 1 }}\u003C\u002Ftemplate>\n          \u003C\u002Fspan>\n          \u003Cspan class=\"step-label\">{{ step.key.replace('gate-', '') }}\u003C\u002Fspan>\n        \u003C\u002Fbutton>\n      \u003C\u002Fli>\n    \u003C\u002Fol>\n\n    \u003Cdiv class=\"progress\">\n      \u003Cdiv class=\"progress-fill\" :style=\"{ width: `${wizard.progress * 100}%` }\">\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cform v-if=\"wizard.currentStep === 'gate-consent'\" class=\"stack\" @submit.prevent>\n      \u003Cp class=\"terms\">\n        By continuing you agree to the terms of service and privacy policy. Every step after this\n        one stays locked until you accept and press Next.\n      \u003C\u002Fp>\n      \u003Clabel class=\"row\">\n        \u003Cinput v-register=\"consent.register('accepted')\" type=\"checkbox\" \u002F>\n        I accept the terms of service \u003Cspan class=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\n      \u003C\u002Flabel>\n      \u003Cem v-if=\"consent.fields.accepted.showErrors\">{{\n        consent.fields.accepted.firstError?.message\n      }}\u003C\u002Fem>\n    \u003C\u002Fform>\n\n    \u003Cform v-else-if=\"wizard.currentStep === 'gate-shipping'\" class=\"stack\" @submit.prevent>\n      \u003Clabel>\n        \u003Cspan>Address \u003Cspan class=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\u003C\u002Fspan>\n        \u003Cinput v-register=\"shipping.register('address')\" autocomplete=\"street-address\" \u002F>\n        \u003Cem v-if=\"shipping.fields.address.showErrors\">{{\n          shipping.fields.address.firstError?.message\n        }}\u003C\u002Fem>\n      \u003C\u002Flabel>\n      \u003Clabel>\n        \u003Cspan>City \u003Cspan class=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\u003C\u002Fspan>\n        \u003Cinput v-register=\"shipping.register('city')\" autocomplete=\"address-level2\" \u002F>\n        \u003Cem v-if=\"shipping.fields.city.showErrors\">{{\n          shipping.fields.city.firstError?.message\n        }}\u003C\u002Fem>\n      \u003C\u002Flabel>\n    \u003C\u002Fform>\n\n    \u003Cform v-else-if=\"wizard.currentStep === 'gate-payment'\" class=\"stack\" @submit.prevent>\n      \u003Clabel>\n        \u003Cspan>Card number \u003Cspan class=\"required\" aria-hidden=\"true\">*\u003C\u002Fspan>\u003C\u002Fspan>\n        \u003Cinput\n          v-register=\"payment.register('cardNumber')\"\n          inputmode=\"numeric\"\n          autocomplete=\"cc-number\"\n        \u002F>\n        \u003Cem v-if=\"payment.fields.cardNumber.showErrors\">{{\n          payment.fields.cardNumber.firstError?.message\n        }}\u003C\u002Fem>\n      \u003C\u002Flabel>\n    \u003C\u002Fform>\n\n    \u003Cdiv class=\"wizard-nav\">\n      \u003Cbutton type=\"button\" class=\"ghost\" :disabled=\"!wizard.canGoBack\" @click=\"wizard.back()\">\n        ← Back\n      \u003C\u002Fbutton>\n      \u003Cspan class=\"step-of\">Step {{ wizard.activeIndex + 1 }} of {{ wizard.count }}\u003C\u002Fspan>\n      \u003Cbutton\n        v-if=\"!wizard.isFinalStep\"\n        type=\"button\"\n        class=\"primary\"\n        :disabled=\"wizard.submitting\"\n        @click=\"wizard.tryNext()\"\n      >\n        Next →\n      \u003C\u002Fbutton>\n      \u003Cbutton v-else type=\"button\" class=\"primary\" :disabled=\"wizard.submitting\" @click=\"onFinish\">\n        {{ wizard.submitting ? 'Submitting…' : 'Place order' }}\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv v-if=\"wizard.done\" class=\"banner success\" role=\"status\">\n      ✓ Order placed. Thanks for confirming.\n    \u003C\u002Fdiv>\n\n    \u003Cp class=\"hint\">\n      One word gates the whole flow: \u003Ccode>gate(consent)\u003C\u002Fcode>. Checking the box is not enough: the\n      rail stays sealed until you confirm by pressing Next. Every later step is then frozen and\n      unreachable until the gate clears, even by a direct link.\n    \u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\n  .demo .step-button {\n    text-transform: capitalize;\n  }\n  .demo form {\n    min-height: 11rem;\n  }\n  .demo .rail li.locked .step-button {\n    cursor: not-allowed;\n    opacity: 0.55;\n  }\n  .demo .terms {\n    margin: 0;\n    color: var(--color-fg-muted);\n    line-height: 1.5;\n  }\n\u003C\u002Fstyle>\n","\u002F* GENERATED by apps\u002Fsite\u002Fscripts\u002Fdemo-styles\u002Fcodegen.mjs (do not edit).\n   Composed from registry.mjs + this demo's styles.json ({ \"with\": [...] }).\n   Regenerated on dev\u002Fbuild and gitignored. See ..\u002FREADME.md. *\u002F\n\n.demo-consent-gate .demo {\n  --color-bg: #ffffff;\n  --color-fg: #101828;\n  --color-fg-muted: #475467;\n  --color-fg-subtle: #667085;\n  --color-surface: #f9fafb;\n  --color-surface-2: #f2f4f7;\n  --color-surface-3: #eaecf0;\n  --color-border: #eaecf0;\n  --color-border-strong: #d0d5dd;\n  --color-accent: #6938ef;\n  --color-accent-hover: #5925dc;\n  --color-accent-fg: #ffffff;\n  --color-accent-soft: #f4f3ff;\n  --color-accent-soft-fg: #5925dc;\n  --color-success: #17b26a;\n  --color-success-soft: #ecfdf3;\n  --color-warning: #f79009;\n  --color-warning-soft: #fffaeb;\n  --color-danger: #f04438;\n  --color-danger-soft: #fef3f2;\n\n  display: flex;\n  flex-direction: column;\n  gap: 0.875rem;\n  max-width: 30rem;\n  color: var(--color-fg);\n  font-size: 0.875rem;\n}\n\n.dark .demo-consent-gate .demo {\n  --color-bg: #0c111d;\n  --color-fg: #f9fafb;\n  --color-fg-muted: #98a2b3;\n  --color-fg-subtle: #667085;\n  --color-surface: #101828;\n  --color-surface-2: #1d2939;\n  --color-surface-3: #344054;\n  --color-border: #1d2939;\n  --color-border-strong: #344054;\n  --color-accent: #7a5af8;\n  --color-accent-hover: #9b8afb;\n  --color-accent-fg: #0c111d;\n  --color-accent-soft: #27115f;\n  --color-accent-soft-fg: #bdb4fe;\n  --color-success: #47cd89;\n  --color-success-soft: #074d31;\n  --color-warning: #fdb022;\n  --color-warning-soft: #7a2e0e;\n  --color-danger: #f97066;\n  --color-danger-soft: #7a271a;\n}\n\n.demo-consent-gate .demo input:where(:not([type='checkbox'], [type='radio'], [type='file'], [type='range'], [type='color'])) {\n  padding: 0.5rem 0.75rem;\n  border: 1px solid var(--color-border);\n  border-radius: 0.375rem;\n  background: var(--color-bg);\n  color: var(--color-fg);\n  font-family: inherit;\n  font-size: 0.875rem;\n}\n.demo-consent-gate .demo input:where(:not([type='checkbox'], [type='radio'], [type='file'], [type='range'], [type='color'])):focus {\n  outline: 2px solid var(--color-accent);\n  outline-offset: -1px;\n}\n.demo-consent-gate .demo input:where(:not([type='checkbox'], [type='radio'], [type='file'], [type='range'], [type='color'])):read-only {\n  background: var(--color-surface);\n  color: var(--color-fg-muted);\n}\n.demo-consent-gate .demo input:where(:not([type='checkbox'], [type='radio'], [type='file'], [type='range'], [type='color'])):disabled {\n  background: var(--color-surface);\n  color: var(--color-fg-subtle);\n  cursor: not-allowed;\n}\n\n.demo-consent-gate .demo label {\n  display: flex;\n  flex-direction: column;\n  gap: 0.25rem;\n  font-size: 0.875rem;\n  font-weight: 500;\n}\n\n.demo-consent-gate .demo button.ghost {\n  align-self: flex-start;\n  margin-top: 0;\n  padding: 0.375rem 0.75rem;\n  border: 1px solid var(--color-border-strong);\n  border-radius: 0.375rem;\n  background: var(--color-bg);\n  color: var(--color-fg);\n  font-size: 0.8125rem;\n  font-weight: 500;\n}\n.demo-consent-gate .demo button.ghost:hover:not(:disabled) {\n  background: var(--color-surface-2);\n}\n.demo-consent-gate .demo button.ghost:disabled {\n  opacity: 0.5;\n  cursor: not-allowed;\n}\n\n.demo-consent-gate .demo button.primary {\n  align-self: flex-start;\n  border: 1px solid var(--color-accent);\n  background: var(--color-accent);\n  color: var(--color-accent-fg);\n  font-weight: 600;\n}\n.demo-consent-gate .demo button.primary:hover:not(:disabled) {\n  background: var(--color-accent-hover);\n  border-color: var(--color-accent-hover);\n}\n.demo-consent-gate .demo .actions button.primary {\n  padding: 0.35rem 0.8rem;\n}\n\n.demo-consent-gate .demo .row {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  gap: 0.5rem;\n  font-size: 0.875rem;\n  font-weight: 400;\n}\n.demo-consent-gate .demo .row input {\n  margin: 0;\n}\n.demo-consent-gate .demo .row.compact {\n  gap: 0.4rem;\n  font-size: 0.75rem;\n  color: var(--color-fg-subtle);\n}\n.demo-consent-gate .demo .row.compact input {\n  width: auto;\n}\n\n.demo-consent-gate .demo em,\n.demo-consent-gate .demo .error {\n  color: var(--color-danger);\n  font-size: 0.8125rem;\n  font-style: normal;\n  font-weight: 400;\n}\n\n.demo-consent-gate .demo .hint {\n  margin: 0;\n  font-size: 0.75rem;\n  line-height: 1.5;\n  color: var(--color-fg-muted);\n}\n\n.demo-consent-gate .demo code {\n  padding: 0.05rem 0.3rem;\n  border-radius: 0.25rem;\n  background: var(--color-surface-2);\n  font-family: ui-monospace, monospace;\n  font-size: 0.9em;\n}\n\n.demo-consent-gate .demo .required {\n  margin-left: 0.125rem;\n  color: var(--color-danger);\n  font-weight: 600;\n}\n\n.demo-consent-gate .demo .banner {\n  padding: 0.5rem 0.75rem;\n  border: 1px solid var(--color-border);\n  border-radius: 0.375rem;\n  background: var(--color-surface);\n  font-size: 0.8125rem;\n  font-weight: 500;\n}\n.demo-consent-gate .demo .banner .chips {\n  margin-left: auto;\n}\n.demo-consent-gate .demo .banner.idle {\n  background: var(--color-surface-2);\n  color: var(--color-fg-muted);\n}\n.demo-consent-gate .demo .banner.pending {\n  background: var(--color-accent-soft);\n  color: var(--color-accent-soft-fg);\n}\n.demo-consent-gate .demo .banner.busy {\n  background: var(--color-warning-soft);\n  color: var(--color-warning);\n}\n.demo-consent-gate .demo .banner.error,\n.demo-consent-gate .demo .banner.failed {\n  background: var(--color-danger-soft);\n  color: var(--color-danger);\n}\n.demo-consent-gate .demo .banner.success,\n.demo-consent-gate .demo .banner.saved {\n  background: var(--color-success-soft);\n  color: var(--color-success);\n}\n\n.demo-consent-gate .demo .stack {\n  display: flex;\n  flex-direction: column;\n  gap: 0.625rem;\n}\n\n.demo-consent-gate .demo .progress {\n  height: 0.375rem;\n  border-radius: 999px;\n  background: var(--color-surface-2);\n  overflow: hidden;\n}\n.demo-consent-gate .demo .progress-fill {\n  height: 100%;\n  background: var(--color-accent);\n  transition: width 200ms ease;\n}\n\n.demo-consent-gate .demo .rail {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.375rem;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n}\n.demo-consent-gate .demo .rail li {\n  display: flex;\n  flex: 1 1 6rem;\n}\n.demo-consent-gate .demo .step-button {\n  flex: 1;\n  display: flex;\n  align-items: center;\n  gap: 0.375rem;\n  padding: 0.375rem 0.5rem;\n  border: 0;\n  border-radius: 0.375rem;\n  background: var(--color-surface-2);\n  color: var(--color-fg-muted);\n  font-family: inherit;\n  font-size: 0.75rem;\n  font-weight: 400;\n  text-align: left;\n  cursor: pointer;\n  transition: filter 120ms ease;\n}\n.demo-consent-gate .demo .step-button:hover {\n  filter: brightness(0.97);\n}\n.demo-consent-gate .demo .step-button:focus-visible {\n  outline: 2px solid var(--color-accent);\n  outline-offset: 2px;\n}\n.demo-consent-gate .demo .rail li.current .step-button {\n  background: var(--color-accent-soft);\n  color: var(--color-accent-soft-fg);\n  font-weight: 500;\n}\n.demo-consent-gate .demo .rail li.done .step-button {\n  background: var(--color-success-soft);\n  color: var(--color-success);\n}\n.demo-consent-gate .demo .step-num {\n  flex: none;\n  width: 1.25rem;\n  height: 1.25rem;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  border-radius: 999px;\n  background: var(--color-bg);\n  font-family: ui-monospace, monospace;\n  font-size: 0.75rem;\n}\n.demo-consent-gate .demo .wizard-nav {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n}\n.demo-consent-gate .demo .wizard-nav button {\n  align-self: auto;\n  margin-top: 0;\n}\n.demo-consent-gate .demo .step-of {\n  flex: 1;\n  text-align: center;\n  font-family: ui-monospace, monospace;\n  font-size: 0.75rem;\n  color: var(--color-fg-muted);\n}\n",1784252108146]