1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[109],{529:(e,t,s)=>{"use strict";s.d(t,{p:()=>a});var r=s(5155),i=s(1442);let a=(0,s(2115).forwardRef)(({className:e,label:t,error:s,id:a,...n},o)=>(0,r.jsxs)("div",{className:"w-full",children:[t&&(0,r.jsx)("label",{htmlFor:a,className:"block font-display font-medium text-sm mb-1.5",children:t}),(0,r.jsx)("input",{ref:o,id:a,className:(0,i.cn)("w-full px-4 py-2.5 rounded-lg border-2 border-border-thick font-body text-sm","bg-white placeholder:text-border-thick/40","focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary",s&&"border-danger focus:ring-danger focus:border-danger",e),...n}),s&&(0,r.jsx)("p",{className:"mt-1 text-sm text-danger font-body",children:s})]}));a.displayName="Input"},775:(e,t,s)=>{"use strict";s.d(t,{DangerZone:()=>l});var r=s(5155),i=s(2115),a=s(1477),n=s(9239),o=s(4193);function l(){let[e,t]=(0,i.useState)(!1);async function s(){t(!0),(0,o.IU)(),await fetch("/api/auth/signout",{method:"POST"}),window.location.href="/login"}return(0,r.jsxs)(a.Zp,{children:[(0,r.jsx)(a.aR,{children:(0,r.jsx)("h2",{className:"font-display text-base font-bold text-danger",children:"Account"})}),(0,r.jsx)(a.Wu,{children:(0,r.jsxs)("div",{className:"flex items-center justify-between",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("p",{className:"font-display text-sm font-semibold",children:"Sign Out"}),(0,r.jsx)("p",{className:"font-body text-xs text-border-thick/50",children:"Sign out of your account on this device"})]}),(0,r.jsx)(n.$,{variant:"destructive",size:"sm",onClick:s,disabled:e,children:e?"Signing out...":"Sign Out"})]})})]})}},1477:(e,t,s)=>{"use strict";s.d(t,{Wu:()=>l,Zp:()=>n,aR:()=>o});var r=s(5155),i=s(1442),a=s(2115);let n=(0,a.forwardRef)(({className:e,variant:t="default",...s},a)=>(0,r.jsx)("div",{ref:a,className:(0,i.cn)("bg-white rounded-xl border-2 border-border-thick","default"===t&&"shadow-brutal",e),...s}));n.displayName="Card";let o=(0,a.forwardRef)(({className:e,...t},s)=>(0,r.jsx)("div",{ref:s,className:(0,i.cn)("px-6 pt-6 pb-2",e),...t}));o.displayName="CardHeader";let l=(0,a.forwardRef)(({className:e,...t},s)=>(0,r.jsx)("div",{ref:s,className:(0,i.cn)("px-6 pb-6",e),...t}));l.displayName="CardContent"},1570:(e,t,s)=>{"use strict";function r(){return"serviceWorker"in navigator&&"PushManager"in window&&"Notification"in window}function i(){return r()?Notification.permission:"unsupported"}async function a(){var e;let t,s;if(!r()||"granted"!==await Notification.requestPermission())return null;let i=await navigator.serviceWorker.ready,a=await i.pushManager.getSubscription();if(a)return a;let n=await fetch("/api/settings/push/vapid");if(!n.ok)return null;let{publicKey:o}=await n.json(),l=await i.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:(e=o,t="=".repeat((4-e.length%4)%4),s=atob((e+t).replace(/-/g,"+").replace(/_/g,"/")),Uint8Array.from(s,e=>e.charCodeAt(0)))});return await fetch("/api/settings/push",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({subscription:l.toJSON()})}),l}async function n(){if(!r())return!1;let e=await navigator.serviceWorker.ready,t=await e.pushManager.getSubscription();if(!t)return!0;let s=await t.unsubscribe();return s&&await fetch("/api/settings/push",{method:"DELETE"}),s}async function o(){if(!r())return!1;let e=await navigator.serviceWorker.ready;return null!==await e.pushManager.getSubscription()}s.d(t,{QQ:()=>n,eA:()=>o,iU:()=>i,jV:()=>r,sO:()=>a})},1747:(e,t,s)=>{Promise.resolve().then(s.bind(s,3799)),Promise.resolve().then(s.bind(s,775)),Promise.resolve().then(s.bind(s,6938)),Promise.resolve().then(s.bind(s,6337)),Promise.resolve().then(s.bind(s,8048))},3280:(e,t,s)=>{"use strict";s.d(t,{l:()=>a});var r=s(5155),i=s(1442);function a({checked:e,onChange:t,label:s,disabled:a,className:n}){return(0,r.jsxs)("label",{className:(0,i.cn)("inline-flex items-center gap-3 cursor-pointer select-none",a&&"opacity-50 cursor-not-allowed",n),children:[(0,r.jsx)("button",{role:"switch",type:"button","aria-checked":e,disabled:a,onClick:()=>t(!e),className:(0,i.cn)("relative w-11 h-6 rounded-full border-2 border-border-thick transition-colors duration-150","focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",e?"bg-primary":"bg-retro-gray",!a&&"cursor-pointer"),children:(0,r.jsx)("span",{className:(0,i.cn)("absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white border border-border-thick transition-transform duration-150",e&&"translate-x-5")})}),s&&(0,r.jsx)("span",{className:"font-body text-sm text-border-thick",children:s})]})}},3799:(e,t,s)=>{"use strict";s.d(t,{BackupSection:()=>o});var r=s(5155),i=s(8500),a=s.n(i),n=s(1477);function o(){return(0,r.jsxs)(n.Zp,{children:[(0,r.jsx)(n.aR,{children:(0,r.jsx)("h2",{className:"font-display text-base font-bold",children:"Backup & Export"})}),(0,r.jsxs)(n.Wu,{children:[(0,r.jsx)("p",{className:"font-body text-sm text-border-thick/70 mb-3",children:"SnapCards is your study home â but your cards are yours. Back up any deck as Anki .apkg (Pro) or CSV (all plans) from the deck page."}),(0,r.jsxs)(a(),{href:"/library",className:"inline-flex items-center gap-1.5 font-display text-sm font-semibold text-primary hover:underline underline-offset-4",children:["Go to library",(0,r.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,r.jsx)("path",{d:"M5 12h14"}),(0,r.jsx)("path",{d:"m12 5 7 7-7 7"})]})]})]})]})}},5160:(e,t,s)=>{"use strict";s.d(t,{E:()=>o});var r=s(5155),i=s(1442),a=s(2115);let n={default:"bg-retro-gray/50 text-border-thick",primary:"bg-primary/10 text-primary",secondary:"bg-safety-orange/10 text-safety-orange",success:"bg-success/10 text-success",warning:"bg-warning/10 text-warning",danger:"bg-danger/10 text-danger"},o=(0,a.forwardRef)(({className:e,variant:t="default",...s},a)=>(0,r.jsx)("span",{ref:a,className:(0,i.cn)("inline-flex items-center rounded-full border border-border-thick/20 px-2.5 py-0.5","font-display text-xs font-semibold tracking-wide",n[t],e),...s}));o.displayName="Badge"},6337:(e,t,s)=>{"use strict";s.d(t,{PlanSection:()=>m});var r=s(5155),i=s(2115),a=s(1477),n=s(9239),o=s(5160),l=s(4057),d=s(7913),c=s(4193),u=s(2744);function m({currentTier:e}){let[t,s]=(0,i.useState)(!1),[m,h]=(0,i.useState)(null),[x,b]=(0,i.useState)(!1),[p,f]=(0,i.useState)(null);async function g(){s(!0),h(null);try{let e=await fetch("/api/billing/portal",{method:"POST"});if(e.ok){let t=await e.json();t.portalUrl&&window.open(t.portalUrl,"_blank")}else h("Unable to open subscription portal. Please try again.")}catch{h("Something went wrong. Please try again.")}s(!1)}return(0,i.useEffect)(()=>{fetch("/api/billing/pricing").then(e=>e.json()).then(e=>f(e)).catch(()=>{})},[]),(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)(a.Zp,{children:[(0,r.jsx)(a.aR,{children:(0,r.jsx)("h2",{className:"font-display text-base font-bold",children:"Subscription Plan"})}),(0,r.jsxs)(a.Wu,{children:[m&&(0,r.jsx)("p",{className:"font-body text-sm text-danger mb-3",role:"alert",children:m}),(0,r.jsx)("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:l.NB.map(s=>{let i=s.tier===e;return(0,r.jsxs)("div",{className:`rounded-xl border-2 p-4 ${i?"border-primary bg-primary/5":s.highlighted?"border-safety-orange":"border-border-thick"}`,children:[(0,r.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,r.jsx)("h3",{className:"font-display font-bold",children:s.name}),(0,r.jsx)("div",{className:"flex items-center gap-1.5",children:i&&(0,r.jsx)(o.E,{variant:"primary",children:"Current"})})]}),(0,r.jsx)("p",{className:"font-display text-2xl font-bold mb-3",children:"pro"===s.tier&&p?`${(0,u.$g)(p.monthly)}
1/mo`:s.price}),(0,r.jsx)("ul",{className:"space-y-1.5 mb-4",children:s.features.map(e=>(0,r.jsxs)("li",{className:"font-body text-xs text-border-thick/60 flex items-center gap-2",children:[(0,r.jsx)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",className:"text-success shrink-0",children:(0,r.jsx)("polyline",{points:"20 6 9 17 4 12"})}),e]},e))}),!i&&"pro"===s.tier&&(0,r.jsx)(n.$,{onClick:()=>{(0,c.Dq)("settings"),b(!0)},size:"sm",className:"w-full",children:"Upgrade to Pro"}),i&&"pro"===s.tier&&(0,r.jsx)(n.$,{onClick:g,disabled:t,variant:"ghost",size:"sm",className:"w-full",children:t?"Loading...":"Manage Subscription"})]},s.tier)})})]})]}),(0,r.jsx)(d.k,{open:x,onClose:()=>b(!1),location:"settings"})]})}},6938:(e,t,s)=>{"use strict";s.d(t,{NotificationSection:()=>l});var r=s(5155),i=s(2115),a=s(3280),n=s(1570),o=s(4193);function l(){let[e,t]=(0,i.useState)(null),[s,l]=(0,i.useState)(!1),[d,c]=(0,i.useState)(!1);(0,i.useEffect)(()=>{(0,n.jV)()&&(t(Notification.permission),(0,n.eA)().then(l).catch(()=>{}))},[]);let u=(0,i.useCallback)(async()=>{c(!0);try{if(s)await (0,n.QQ)()&&(l(!1),(0,o.nI)("settings"));else if(await (0,n.sO)())l(!0),t("granted"),(0,o.RY)("granted","settings");else{let e=(0,n.iU)();t("unsupported"===e?null:e),(0,o.RY)("denied"===e?"denied":"dismissed","settings")}}finally{c(!1)}},[s]);return null===e?(0,r.jsxs)("div",{className:"bg-warm-white border-2 border-border-thick rounded-xl shadow-brutal-sm p-6",children:[(0,r.jsx)("h2",{className:"font-display text-lg font-bold mb-1",children:"Push Notifications"}),(0,r.jsxs)("p",{className:"font-body text-sm text-border-thick/50",children:["Push n
1otifications aren't supported in your browser.","u">typeof navigator&&/iPhone|iPad/.test(navigator.userAgent)&&" On iOS, install SnapCards to your home screen first to enable push notifications."]})]}):(0,r.jsxs)("div",{className:"bg-warm-white border-2 border-border-thick rounded-xl shadow-brutal-sm p-6",children:[(0,r.jsxs)("div",{className:"flex items-start justify-between gap-4",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("h2",{className:"font-display text-lg font-bold mb-1",children:"Push Notifications"}),(0,r.jsx)("p",{className:"font-body text-sm text-border-thick/50",children:"Get reminders when cards are due and streak alerts on your device."})]}),(0,r.jsx)(a.l,{checked:s,onChange:u,disabled:d||"denied"===e})]}),"denied"===e&&(0,r.jsx)("div",{className:"mt-3 bg-danger/5 border-2 border-danger/20 rounded-lg px-3 py-2",children:(0,r.jsx)("p",{className:"font-body text-xs text-danger",children:"Notifications are blocked. Please enable them in your browser settings."})}),s&&(0,r.jsx)("div",{className:"mt-3 font-body text-xs text-border-thick/50",children:"You'll receive push notifications for: due card reminders, streak alerts, and weekly progress summaries."})]})}},8048:(e,t,s)=>{"use strict";s.d(t,{ProfileSection:()=>u});var r=s(5155),i=s(2115),a=s(1477),n=s(529),o=s(9239),l=s(5160),d=s(3280),c=s(8884);function u({profile:e,email:t}){let[s,u]=(0,i.useState)(e.display_name||""),[m,h]=(0,i.useState)(e.email_opt_in),[x,b]=(0,i.useState)(e.daily_study_goal),[p,f]=(0,i.useState)(e.session_size||10),[g,y]=(0,i.useState)(!1),[j,N]=(0,i.useState)(!1),[v,k]=(0,i.useState)(null);async function w(){y(!0),k(null);try{(await fetch("/api/settings/profile",{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({display_name:s,email_opt_in:m,daily_study_goal:Math.max(5,Math.min(100,x)),session_size:Math.max(5,Math.min(50,p))})})).ok?(N(!0),setTimeout(()=>N(!1),4e3)):k("Failed to save changes. Please try again.")}catch{k("Something went wrong. Please try again.")}y(!1)}let S="pro"===e.subscription_tier?"primary":"default";return(0,r.jsxs)(a.Zp,{children:[(0,r.jsx)(a.aR,{children:(0,r.jsxs)("div",{className:"flex items-center justify-between",children:[(0,r.jsx)("h2",{className:"font-display text-base font-bold",children:"Profile"}),(0,r.jsx)(l.E,{variant:S,children:e.subscription_tier})]})}),(0,r.jsx)(a.Wu,{children:(0,r.jsx)("fieldset",{disabled:g,className:g?"opacity-60":"",children:(0,r.jsxs)("div",{className:"space-y-4",children:[(0,r.jsx)("div",{children:(0,r.jsx)(n.p,{label:"Display Name",value:s,onChange:e=>u(e.target.value),placeholder:"Your name"})}),(0,r.jsxs)("div",{children:[(0,r.jsx)("label",{className:"block font-display font-medium text-sm mb-1.5",children:"Email"}),(0,r.jsx)("p",{className:"font-body text-sm text-border-thick/50 px-4 py-2.5 rounded-lg border-2 border-retro-gray bg-retro-gray/20",children:t})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("label",{className:"block font-display font-medium text-sm mb-1.5",children:"PDFs Uploaded This Month"}),(0,r.jsxs)("p",{className:"font-body text-sm text-border-thick/50",children:[e.pdfs_uploaded_this_month,"free"===e.subscription_tier&&" / 3"]})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("label",{className:"block font-display font-medium text-sm mb-1.5",children:"Daily Study Goal"}),(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("input",{type:"number",min:5,max:100,value:x,onChange:e=>b(Number(e.target.value)),className:"w-20 px-3 py-2 rounded-lg border-2 border-border-thick font-body text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"}),(0,r.jsx)("span",{className:"font-body text-sm text-border-thick/50",children:"cards per day"})]})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("label",{className:"block font-display font-medium text-sm mb-1.5",children:"Cards Per Session"}),"free"===e.subscription_tier?(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("span",{className:"w-20 px-3 py-2 rounded-lg border-2 border-retro-gray bg-retro-gray/20 font-body text-sm text-border-thick/50",children:c.HK.free.sessionSize}),(0,r.jsx)("span",{className:"font-body text-sm text-border-thick/50",children:"cards per session"}),(0,r.jsx)("span",{className:"font-body text-xs text-border-thick/40",children:"(fixed on Free)"})]}):(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("input",{type:"number",min:5,max:50,value:p,onChange:e=>f(Number(e.target.value)),className:"w-20 px-3 py-2 rounded-lg border-2 border-border-thick font-body text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"}),(0,r.jsx)("span",{className:"font-body text-sm text-border-thick/50",children:"cards per session"})]})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("label",{className:"block font-display font-medium text-sm mb-1.5",children:"Timezone"}),(0,r.jsx)("p",{className:"font-body text-sm text-border-thick/50 px-4 py-2.5 rounded-lg border-2 border-retro-gray bg-retro-gray/20",children:e.timezone||"UTC"}),(0,r.jsx)("p",{className:"font-body text-xs text-border-thick/50 mt-1",children:"Auto-detected from your browser"})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)(d.l,{checked:m,onChange:h,label:"Study reminder emails"}),(0,r.jsx)("p",{className:"font-body text-xs text-border-thick/50 mt-1 ml-14",children:"Weekly summaries, streak alerts, and review reminders"})]}),v&&(0,r.jsx)("p",{className:"font-body text-sm text-danger",role:"alert",children:v}),(0,r.jsx)("div",{className:"flex items-center gap-3",children:(0,r.jsx)(o.$,{onClick:w,disabled:g,size:"sm",children:g?"Saving...":j?"Saved!":"Save Changes"})})]})})})]})}}},e=>{e.O(0,[8500,7913,8441,3139,7358],()=>e(e.s=1747)),_N_E=e.O()}]); 2//# sourceMappingURL=page-87b269219a4216db.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.