{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "loan-calculator",
  "title": "Loan Calculator",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/loan-calculator/LoanCalculator.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport {\n  ArrowRight,\n  Building2,\n  Calculator,\n  Calendar,\n  Car,\n  CheckCircle2,\n  DollarSign,\n  FileSpreadsheet,\n  Home,\n  PiggyBank,\n  Wallet,\n} from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { Separator } from '@/components/ui/separator'\nimport { Slider } from '@/components/ui/slider'\nimport { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from '@/components/ui/table'\nimport { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'\n\ntype LoanType = 'mortgage-30' | 'commercial-10' | 'auto-5' | 'personal-3'\n\ninterface LoanPreset {\n  id: LoanType\n  name: string\n  amount: number\n  rate: number\n  termYears: number\n  extraPayment: number\n  description: string\n}\n\nconst LOAN_PRESETS: Record<LoanType, LoanPreset> = {\n  'mortgage-30': {\n    id: 'mortgage-30',\n    name: 'Mortgage 30Y',\n    amount: 450_000,\n    rate: 6.5,\n    termYears: 30,\n    extraPayment: 200,\n    description: '30-year fixed home mortgage',\n  },\n  'commercial-10': {\n    id: 'commercial-10',\n    name: 'Commercial 10Y',\n    amount: 1_200_000,\n    rate: 7.25,\n    termYears: 10,\n    extraPayment: 500,\n    description: '10-year commercial real estate loan',\n  },\n  'auto-5': {\n    id: 'auto-5',\n    name: 'Auto Loan 5Y',\n    amount: 45_000,\n    rate: 5.9,\n    termYears: 5,\n    extraPayment: 50,\n    description: '60-month standard vehicle loan',\n  },\n  'personal-3': {\n    id: 'personal-3',\n    name: 'Personal Loan',\n    amount: 25_000,\n    rate: 9.5,\n    termYears: 3,\n    extraPayment: 25,\n    description: '36-month fixed unsecured personal loan',\n  },\n}\n\nconst LOAN_AMOUNT_MARKS = {\n  100000: '$100K',\n  500000: '$500K',\n  1000000: '$1M',\n  2000000: '$2M',\n}\n\nconst INTEREST_RATE_MARKS = {\n  1: '1%',\n  5: '5%',\n  10: '10%',\n  15: '15%',\n}\n\nconst LOAN_TERM_MARKS = {\n  5: '5Y',\n  10: '10Y',\n  15: '15Y',\n  20: '20Y',\n  30: '30Y',\n}\n\nconst EXTRA_PAYMENT_MARKS = {\n  0: '$0',\n  500: '$500',\n  1000: '$1K',\n  2000: '$2K',\n}\n\nconst TERM_PRESETS = [5, 10, 15, 20, 30]\n\ninterface MonthRecord {\n  month: number\n  principalPaid: number\n  interestPaid: number\n  extraPaid: number\n  totalPaid: number\n  remainingBalance: number\n}\n\ninterface YearRecord {\n  year: number\n  principalPaid: number\n  interestPaid: number\n  extraPaid: number\n  totalPaid: number\n  remainingBalance: number\n}\n\nfunction fmtCurrency(val: number): string {\n  return new Intl.NumberFormat('en-US', {\n    style: 'currency',\n    currency: 'USD',\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n  }).format(val || 0)\n}\n\nfunction fmtCurrencyWhole(val: number): string {\n  return new Intl.NumberFormat('en-US', {\n    style: 'currency',\n    currency: 'USD',\n    minimumFractionDigits: 0,\n    maximumFractionDigits: 0,\n  }).format(val || 0)\n}\n\nexport function LoanCalculator({ className }: { className?: string }) {\n  const [selectedLoanType, setSelectedLoanType] = React.useState<LoanType>('mortgage-30')\n  const [loanAmount, setLoanAmount] = React.useState(450_000)\n  const [interestRate, setInterestRate] = React.useState(6.5)\n  const [loanTermYears, setLoanTermYears] = React.useState(30)\n  const [extraMonthlyPayment, setExtraMonthlyPayment] = React.useState(200)\n  const [showAllYears, setShowAllYears] = React.useState(false)\n\n  const handleTabChange = React.useCallback((value: string) => {\n    const type = value as LoanType\n    setSelectedLoanType(type)\n    const preset = LOAN_PRESETS[type]\n    if (preset) {\n      setLoanAmount(preset.amount)\n      setInterestRate(preset.rate)\n      setLoanTermYears(preset.termYears)\n      setExtraMonthlyPayment(preset.extraPayment)\n    }\n  }, [])\n\n  // Base monthly payment (standard amortization without extra payments)\n  const baseMonthlyPayment = React.useMemo(() => {\n    const principal = loanAmount\n    const annualRate = interestRate\n    const termYears = loanTermYears\n    const totalMonths = termYears * 12\n\n    if (principal <= 0 || totalMonths <= 0) return 0\n    if (annualRate <= 0) return principal / totalMonths\n\n    const monthlyRate = annualRate / 100 / 12\n    const factor = Math.pow(1 + monthlyRate, totalMonths)\n    if (!isFinite(factor) || factor <= 1) return principal / totalMonths\n    return (principal * (monthlyRate * factor)) / (factor - 1)\n  }, [loanAmount, interestRate, loanTermYears])\n\n  // Total standard cost and interest without extra payment\n  const totalCostBase = React.useMemo(() => {\n    return baseMonthlyPayment * loanTermYears * 12\n  }, [baseMonthlyPayment, loanTermYears])\n\n  const totalInterestBase = React.useMemo(() => {\n    return Math.max(0, totalCostBase - loanAmount)\n  }, [totalCostBase, loanAmount])\n\n  // Month-by-month amortization schedule calculation\n  const amortizationResult = React.useMemo(() => {\n    const principal = Math.max(0, loanAmount)\n    const annualRate = Math.max(0, interestRate)\n    const termYears = Math.max(1, loanTermYears)\n    const extra = Math.max(0, extraMonthlyPayment)\n    const basePayment = baseMonthlyPayment\n    const monthlyRate = annualRate / 100 / 12\n    const standardMonths = termYears * 12\n\n    let balance = principal\n    const monthlySchedule: MonthRecord[] = []\n    let totalInterest = 0\n    let totalPaid = 0\n    let totalExtra = 0\n    let month = 0\n    const maxMonths = 1200 // 100 years safeguard\n\n    while (balance > 0.005 && month < maxMonths) {\n      month++\n      const interestForMonth = annualRate > 0 ? balance * monthlyRate : 0\n      let scheduledTotal = basePayment + extra\n\n      if (balance + interestForMonth < scheduledTotal) {\n        scheduledTotal = balance + interestForMonth\n      }\n\n      const principalForMonth = Math.min(balance, Math.max(0, scheduledTotal - interestForMonth))\n      const extraForMonth = Math.max(0, scheduledTotal - basePayment)\n      balance = Math.max(0, balance - principalForMonth)\n\n      totalInterest += interestForMonth\n      totalPaid += scheduledTotal\n      totalExtra += extraForMonth\n\n      monthlySchedule.push({\n        month,\n        principalPaid: principalForMonth,\n        interestPaid: interestForMonth,\n        extraPaid: extraForMonth,\n        totalPaid: scheduledTotal,\n        remainingBalance: balance,\n      })\n    }\n\n    // Aggregate by year\n    const yearlySchedule: YearRecord[] = []\n    const totalYearsActual = Math.ceil(monthlySchedule.length / 12)\n\n    for (let y = 1; y <= totalYearsActual; y++) {\n      const startIdx = (y - 1) * 12\n      const endIdx = Math.min(y * 12, monthlySchedule.length)\n      const yearMonths = monthlySchedule.slice(startIdx, endIdx)\n\n      if (yearMonths.length === 0) continue\n\n      const yearPrincipal = yearMonths.reduce((sum, m) => sum + m.principalPaid, 0)\n      const yearInterest = yearMonths.reduce((sum, m) => sum + m.interestPaid, 0)\n      const yearExtra = yearMonths.reduce((sum, m) => sum + m.extraPaid, 0)\n      const yearTotal = yearMonths.reduce((sum, m) => sum + m.totalPaid, 0)\n      const endingBalance = yearMonths[yearMonths.length - 1]?.remainingBalance ?? 0\n\n      yearlySchedule.push({\n        year: y,\n        principalPaid: yearPrincipal,\n        interestPaid: yearInterest,\n        extraPaid: yearExtra,\n        totalPaid: yearTotal,\n        remainingBalance: endingBalance,\n      })\n    }\n\n    const monthsSaved = Math.max(0, standardMonths - monthlySchedule.length)\n    const yearsSaved = monthsSaved / 12\n    const interestSaved = Math.max(0, totalInterestBase - totalInterest)\n\n    return {\n      monthlySchedule,\n      yearlySchedule,\n      actualMonths: monthlySchedule.length,\n      actualYears: (monthlySchedule.length / 12).toFixed(1),\n      monthsSaved,\n      yearsSaved: yearsSaved.toFixed(1),\n      totalInterestPaid: totalInterest,\n      totalLoanCost: totalPaid,\n      totalExtraPaid: totalExtra,\n      interestSavings: interestSaved,\n    }\n  }, [loanAmount, interestRate, loanTermYears, extraMonthlyPayment, baseMonthlyPayment, totalInterestBase])\n\n  // Visual breakdown percentages\n  const principalPercent = React.useMemo(() => {\n    const total = amortizationResult.totalLoanCost\n    if (total <= 0) return 100\n    return Math.min(100, Math.max(0, Math.round((loanAmount / total) * 100)))\n  }, [amortizationResult.totalLoanCost, loanAmount])\n\n  const interestPercent = React.useMemo(() => {\n    return Math.max(0, 100 - principalPercent)\n  }, [principalPercent])\n\n  // Filtered schedule for display\n  const displayedSchedule = React.useMemo(() => {\n    if (showAllYears) {\n      return amortizationResult.yearlySchedule\n    }\n    return amortizationResult.yearlySchedule.slice(0, 5)\n  }, [showAllYears, amortizationResult.yearlySchedule])\n\n  // Totals for the displayed schedule\n  const displayedTotals = React.useMemo(() => {\n    const list = displayedSchedule\n    return {\n      principal: list.reduce((sum, r) => sum + r.principalPaid, 0),\n      interest: list.reduce((sum, r) => sum + r.interestPaid, 0),\n      extra: list.reduce((sum, r) => sum + r.extraPaid, 0),\n      total: list.reduce((sum, r) => sum + r.totalPaid, 0),\n    }\n  }, [displayedSchedule])\n\n  return (\n    <div data-slot=\"loan-calculator\" className={cn('mx-auto w-full max-w-6xl space-y-10 p-4 sm:p-6 lg:p-8', className)}>\n      {/* Header */}\n      <div className=\"flex flex-col items-center gap-4 text-center\">\n        <Badge variant=\"outline\" className=\"gap-1.5 px-3 py-1 text-xs font-medium tracking-wider uppercase\">\n          <Calculator className=\"text-primary size-3.5\" />\n          Loan Amortization Engine\n        </Badge>\n        <div className=\"space-y-2\">\n          <h2 className=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl lg:text-4xl\">\n            Loan &amp; Mortgage Amortization Calculator\n          </h2>\n          <p className=\"text-muted-foreground mx-auto max-w-2xl text-sm sm:text-base\">\n            Estimate monthly payments, interest vs principal breakdown, and payoff timelines with extra payments.\n          </p>\n        </div>\n\n        {/* Loan Type Tabs */}\n        <div className=\"mt-2 flex w-full justify-center\">\n          <Tabs value={selectedLoanType} onValueChange={handleTabChange} className=\"w-full sm:w-auto\">\n            <TabsList variant=\"segmented\" className=\"grid w-full grid-cols-2 sm:flex sm:w-auto\">\n              <TabsTrigger value=\"mortgage-30\" className=\"gap-1.5\">\n                <Home className=\"size-3.5\" />\n                Mortgage 30Y\n              </TabsTrigger>\n              <TabsTrigger value=\"commercial-10\" className=\"gap-1.5\">\n                <Building2 className=\"size-3.5\" />\n                Commercial 10Y\n              </TabsTrigger>\n              <TabsTrigger value=\"auto-5\" className=\"gap-1.5\">\n                <Car className=\"size-3.5\" />\n                Auto Loan 5Y\n              </TabsTrigger>\n              <TabsTrigger value=\"personal-3\" className=\"gap-1.5\">\n                <Wallet className=\"size-3.5\" />\n                Personal Loan\n              </TabsTrigger>\n            </TabsList>\n          </Tabs>\n        </div>\n      </div>\n\n      {/* 2-Column Calculator Layout */}\n      <div className=\"grid grid-cols-1 gap-8 lg:grid-cols-12 lg:items-start\">\n        {/* Left Inputs Card */}\n        <div className=\"space-y-6 lg:col-span-7\">\n          <Card>\n            <CardHeader>\n              <CardTitle className=\"flex items-center gap-2\">\n                <DollarSign className=\"text-primary size-5\" />\n                Loan Parameters\n              </CardTitle>\n              <CardDescription>\n                Customize loan principal, interest rate, term duration, and extra monthly contributions.\n              </CardDescription>\n            </CardHeader>\n            <CardContent className=\"space-y-8\">\n              {/* 1. Loan Amount */}\n              <div className=\"space-y-4\">\n                <div className=\"flex items-center justify-between gap-4\">\n                  <div>\n                    <span className=\"text-foreground text-sm font-medium\">Loan Amount (Principal)</span>\n                    <p className=\"text-muted-foreground text-xs\">Total borrowed loan amount.</p>\n                  </div>\n                  <div className=\"w-36 sm:w-44\">\n                    <Input\n                      id=\"loan-amount-input\"\n                      type=\"number\"\n                      size=\"small\"\n                      min={5000}\n                      max={2500000}\n                      step={5000}\n                      prefix=\"$\"\n                      value={loanAmount}\n                      onChange={(e) => setLoanAmount(Number(e.target.value) || 0)}\n                    />\n                  </div>\n                </div>\n                <div className=\"pb-6\">\n                  <Slider\n                    value={[loanAmount]}\n                    min={10000}\n                    max={2000000}\n                    step={5000}\n                    marks={LOAN_AMOUNT_MARKS}\n                    tooltip={(val) => `$${val.toLocaleString('en-US')}`}\n                    onValueChange={(val) => setLoanAmount(val[0])}\n                  />\n                </div>\n                <div className=\"flex flex-wrap items-center gap-1.5 pt-1\">\n                  <span className=\"text-muted-foreground text-xs\">Quick select:</span>\n                  {[150000, 300000, 450000, 750000, 1200000].map((amt) => (\n                    <Button\n                      key={amt}\n                      variant=\"outline\"\n                      size=\"sm\"\n                      className=\"h-6 px-2 text-xs\"\n                      onClick={() => setLoanAmount(amt)}\n                    >\n                      ${amt >= 1000000 ? `${(amt / 1000000).toFixed(1)}M` : `${amt / 1000}K`}\n                    </Button>\n                  ))}\n                </div>\n              </div>\n\n              <Separator />\n\n              {/* 2. Interest Rate */}\n              <div className=\"space-y-4\">\n                <div className=\"flex items-center justify-between gap-4\">\n                  <div>\n                    <span className=\"text-foreground text-sm font-medium\">Annual Interest Rate (APR)</span>\n                    <p className=\"text-muted-foreground text-xs\">Fixed annual percentage rate.</p>\n                  </div>\n                  <div className=\"w-32 sm:w-36\">\n                    <Input\n                      id=\"interest-rate-input\"\n                      type=\"number\"\n                      size=\"small\"\n                      min={0.5}\n                      max={25}\n                      step={0.1}\n                      suffix=\"%\"\n                      value={interestRate}\n                      onChange={(e) => setInterestRate(Number(e.target.value) || 0)}\n                    />\n                  </div>\n                </div>\n                <div className=\"pb-6\">\n                  <Slider\n                    value={[interestRate]}\n                    min={1}\n                    max={15}\n                    step={0.1}\n                    marks={INTEREST_RATE_MARKS}\n                    tooltip={(val) => `${val.toFixed(1)}%`}\n                    onValueChange={(val) => setInterestRate(val[0])}\n                  />\n                </div>\n                <div className=\"flex flex-wrap items-center gap-1.5 pt-1\">\n                  <span className=\"text-muted-foreground text-xs\">Market benchmarks:</span>\n                  {[4.5, 5.5, 6.5, 7.5, 9.5].map((rate) => (\n                    <Button\n                      key={rate}\n                      variant=\"outline\"\n                      size=\"sm\"\n                      className=\"h-6 px-2 text-xs\"\n                      onClick={() => setInterestRate(rate)}\n                    >\n                      {rate}%\n                    </Button>\n                  ))}\n                </div>\n              </div>\n\n              <Separator />\n\n              {/* 3. Loan Term */}\n              <div className=\"space-y-4\">\n                <div className=\"flex items-center justify-between gap-4\">\n                  <div>\n                    <span className=\"text-foreground text-sm font-medium\">Loan Term</span>\n                    <p className=\"text-muted-foreground text-xs\">Amortization period in years.</p>\n                  </div>\n                  <div className=\"flex items-center gap-1.5\">\n                    <Badge variant=\"secondary\" className=\"font-mono text-xs font-semibold\">\n                      {loanTermYears} Years ({loanTermYears * 12} mo)\n                    </Badge>\n                  </div>\n                </div>\n                <div className=\"pb-6\">\n                  <Slider\n                    value={[loanTermYears]}\n                    min={1}\n                    max={30}\n                    step={1}\n                    marks={LOAN_TERM_MARKS}\n                    tooltip={(val) => `${val} Years`}\n                    onValueChange={(val) => setLoanTermYears(val[0])}\n                  />\n                </div>\n                <div className=\"flex flex-wrap items-center gap-1.5 pt-1\">\n                  <span className=\"text-muted-foreground text-xs\">Standard terms:</span>\n                  {TERM_PRESETS.map((term) => (\n                    <Button\n                      key={term}\n                      variant={loanTermYears === term ? 'default' : 'outline'}\n                      size=\"sm\"\n                      className=\"h-6 px-2 text-xs\"\n                      onClick={() => setLoanTermYears(term)}\n                    >\n                      {term} Yrs\n                    </Button>\n                  ))}\n                </div>\n              </div>\n\n              <Separator />\n\n              {/* 4. Extra Monthly Payment */}\n              <div className=\"space-y-4\">\n                <div className=\"flex flex-wrap items-center justify-between gap-4\">\n                  <div>\n                    <div className=\"flex items-center gap-2\">\n                      <span className=\"text-foreground text-sm font-medium\">Extra Monthly Payment</span>\n                      <Badge\n                        variant=\"outline\"\n                        className=\"border-success/20 bg-success/10 text-success text-xs font-normal\"\n                      >\n                        Accelerates Payoff\n                      </Badge>\n                    </div>\n                    <p className=\"text-muted-foreground text-xs\">Directly reduces principal balance each month.</p>\n                  </div>\n                  <div className=\"w-36 sm:w-44\">\n                    <Input\n                      id=\"extra-payment-input\"\n                      type=\"number\"\n                      size=\"small\"\n                      min={0}\n                      max={10000}\n                      step={25}\n                      prefix=\"$\"\n                      suffix=\"/ mo\"\n                      value={extraMonthlyPayment}\n                      onChange={(e) => setExtraMonthlyPayment(Number(e.target.value) || 0)}\n                    />\n                  </div>\n                </div>\n                <div className=\"pb-6\">\n                  <Slider\n                    value={[extraMonthlyPayment]}\n                    min={0}\n                    max={2000}\n                    step={25}\n                    marks={EXTRA_PAYMENT_MARKS}\n                    tooltip={(val) => `$${val}/mo`}\n                    onValueChange={(val) => setExtraMonthlyPayment(val[0])}\n                  />\n                </div>\n                <div className=\"flex flex-wrap items-center gap-1.5 pt-1\">\n                  <span className=\"text-muted-foreground text-xs\">Quick extra:</span>\n                  {[0, 100, 200, 500, 1000].map((extra) => (\n                    <Button\n                      key={extra}\n                      variant=\"outline\"\n                      size=\"sm\"\n                      className=\"h-6 px-2 text-xs\"\n                      onClick={() => setExtraMonthlyPayment(extra)}\n                    >\n                      +${extra}/mo\n                    </Button>\n                  ))}\n                </div>\n              </div>\n            </CardContent>\n          </Card>\n        </div>\n\n        {/* Right Results Card (Sticky on desktop) */}\n        <div className=\"lg:sticky lg:top-8 lg:col-span-5\">\n          <Card className=\"border-border bg-card shadow-xs\">\n            <CardHeader className=\"pb-4\">\n              <div className=\"flex items-center justify-between\">\n                <span className=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">\n                  Estimated Monthly Payment\n                </span>\n                <Badge\n                  variant=\"secondary\"\n                  className=\"bg-primary/10 text-primary border-primary/20 text-xs font-semibold\"\n                >\n                  Amortized\n                </Badge>\n              </div>\n              <div className=\"mt-4\">\n                <div className=\"flex items-baseline gap-1.5\">\n                  <span className=\"text-foreground text-4xl font-bold tracking-tight tabular-nums sm:text-5xl\">\n                    {fmtCurrency(baseMonthlyPayment)}\n                  </span>\n                  <span className=\"text-muted-foreground text-sm font-normal\"> / month</span>\n                </div>\n                {extraMonthlyPayment > 0 ? (\n                  <p className=\"text-muted-foreground mt-2 text-xs\">\n                    Total monthly cash outlay:{' '}\n                    <span className=\"text-foreground font-semibold tabular-nums\">\n                      {fmtCurrency(baseMonthlyPayment + extraMonthlyPayment)}/mo\n                    </span>{' '}\n                    ({fmtCurrency(baseMonthlyPayment)} base + {fmtCurrency(extraMonthlyPayment)} extra)\n                  </p>\n                ) : (\n                  <p className=\"text-muted-foreground mt-2 text-xs\">\n                    Standard fixed payment based on {interestRate}% APR over {loanTermYears} years.\n                  </p>\n                )}\n              </div>\n            </CardHeader>\n\n            <CardContent className=\"space-y-5\">\n              <Separator />\n\n              {/* Extra Payment Impact Box */}\n              {extraMonthlyPayment > 0 && amortizationResult.interestSavings > 0 && (\n                <div className=\"border-success/20 bg-success/10 text-success rounded-lg border p-3.5 text-xs\">\n                  <div className=\"flex items-start gap-2.5\">\n                    <PiggyBank className=\"mt-0.5 size-4 shrink-0\" />\n                    <div className=\"space-y-1\">\n                      <p className=\"text-sm leading-snug font-semibold\">\n                        Saves {fmtCurrencyWhole(amortizationResult.interestSavings)} and cuts{' '}\n                        {amortizationResult.yearsSaved} years off payoff!\n                      </p>\n                      <p className=\"text-success/80 leading-normal\">\n                        You'll be debt-free in {amortizationResult.actualYears} years ({amortizationResult.actualMonths}{' '}\n                        payments) instead of {loanTermYears} years.\n                      </p>\n                    </div>\n                  </div>\n                </div>\n              )}\n\n              {/* Summary Breakdown */}\n              <div className=\"space-y-2.5\">\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">Total Loan Principal</span>\n                  <span className=\"font-medium tabular-nums\">{fmtCurrency(loanAmount)}</span>\n                </div>\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">Total Interest Paid</span>\n                  <span className=\"text-warning text-warning font-medium tabular-nums\">\n                    {fmtCurrency(amortizationResult.totalInterestPaid)}\n                  </span>\n                </div>\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">Total Loan Cost</span>\n                  <span className=\"text-foreground font-semibold tabular-nums\">\n                    {fmtCurrency(amortizationResult.totalLoanCost)}\n                  </span>\n                </div>\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">Effective Payoff Timeline</span>\n                  <span className=\"font-medium tabular-nums\">\n                    {amortizationResult.actualYears} Years ({amortizationResult.actualMonths} mo)\n                  </span>\n                </div>\n              </div>\n\n              <Separator />\n\n              {/* Visual Principal vs Interest Bar */}\n              <div className=\"space-y-2.5\">\n                <div className=\"flex items-center justify-between text-xs font-medium\">\n                  <span className=\"text-foreground\">Principal vs. Total Interest</span>\n                  <span className=\"text-muted-foreground tabular-nums\">\n                    {principalPercent}% / {interestPercent}%\n                  </span>\n                </div>\n                <div className=\"bg-muted relative flex h-3 w-full overflow-hidden rounded-full shadow-inner\">\n                  <div\n                    className=\"bg-primary h-full transition-[width] duration-300\"\n                    style={{ width: `${principalPercent}%` }}\n                  />\n                  <div\n                    className=\"bg-warning h-full transition-[width] duration-300\"\n                    style={{ width: `${interestPercent}%` }}\n                  />\n                </div>\n                <div className=\"text-muted-foreground flex flex-col gap-1.5 text-xs sm:flex-row sm:items-center sm:justify-between\">\n                  <div className=\"flex items-center gap-1.5\">\n                    <span className=\"bg-primary size-2 shrink-0 rounded-full\" />\n                    <span>\n                      Principal: {fmtCurrencyWhole(loanAmount)} ({principalPercent}%)\n                    </span>\n                  </div>\n                  <div className=\"flex items-center gap-1.5\">\n                    <span className=\"bg-warning size-2 shrink-0 rounded-full\" />\n                    <span>\n                      Interest: {fmtCurrencyWhole(amortizationResult.totalInterestPaid)} ({interestPercent}%)\n                    </span>\n                  </div>\n                </div>\n              </div>\n\n              <Separator />\n\n              {/* Key Features / Trust Checklist */}\n              <ul className=\"text-muted-foreground space-y-2 text-xs\">\n                <li className=\"flex items-center gap-2\">\n                  <CheckCircle2 className=\"text-primary size-3.5 shrink-0\" />\n                  <span>Real-time compound interest calculations</span>\n                </li>\n                <li className=\"flex items-center gap-2\">\n                  <CheckCircle2 className=\"text-primary size-3.5 shrink-0\" />\n                  <span>Principal reduction extra payment model</span>\n                </li>\n                <li className=\"flex items-center gap-2\">\n                  <CheckCircle2 className=\"text-primary size-3.5 shrink-0\" />\n                  <span>Standard fixed-rate banking amortization schedule</span>\n                </li>\n              </ul>\n            </CardContent>\n\n            <CardFooter className=\"flex flex-col gap-2.5 pt-2\">\n              <Button className=\"w-full gap-2 font-semibold shadow-xs\" size=\"lg\">\n                Apply for Pre-Approval\n                <ArrowRight className=\"size-4\" />\n              </Button>\n              <Button aria-label=\"Download attachment\" variant=\"outline\" className=\"w-full gap-2\" size=\"default\">\n                <FileSpreadsheet className=\"size-4\" />\n                Download Schedule (CSV)\n              </Button>\n            </CardFooter>\n          </Card>\n        </div>\n      </div>\n\n      {/* Amortization Schedule Table */}\n      <Card className=\"mt-8\">\n        <CardHeader className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <CardTitle className=\"flex items-center gap-2\">\n              <Calendar className=\"text-primary size-5\" />\n              Amortization Schedule\n            </CardTitle>\n            <CardDescription>\n              Annual breakdown of principal reduction, interest paid, extra contributions, and ending balance.\n            </CardDescription>\n          </div>\n          <div className=\"flex items-center gap-2\">\n            <Button variant=\"outline\" size=\"sm\" className=\"text-xs\" onClick={() => setShowAllYears((prev) => !prev)}>\n              {showAllYears ? 'Show First 5 Years' : `Show All ${amortizationResult.yearlySchedule.length} Years`}\n            </Button>\n          </div>\n        </CardHeader>\n        <CardContent>\n          <div className=\"overflow-x-auto\">\n            <Table density=\"cozy\">\n              <TableHeader>\n                <TableRow>\n                  <TableHead>Year</TableHead>\n                  <TableHead className=\"text-right\">Principal Paid</TableHead>\n                  <TableHead className=\"text-right\">Interest Paid</TableHead>\n                  <TableHead className=\"text-right\">Extra Principal</TableHead>\n                  <TableHead className=\"text-right\">Total Payment</TableHead>\n                  <TableHead className=\"text-right\">Ending Balance</TableHead>\n                </TableRow>\n              </TableHeader>\n              <TableBody>\n                {displayedSchedule.map((row) => (\n                  <TableRow key={row.year}>\n                    <TableCell className=\"font-medium\">\n                      <div className=\"flex items-center gap-2\">\n                        <span>Year {row.year}</span>\n                        {row.year === 1 && (\n                          <Badge variant=\"outline\" className=\"text-xs font-normal\">\n                            Initial\n                          </Badge>\n                        )}\n                      </div>\n                    </TableCell>\n                    <TableCell className=\"text-foreground text-right font-medium tabular-nums\">\n                      {fmtCurrency(row.principalPaid)}\n                    </TableCell>\n                    <TableCell className=\"text-warning text-warning text-right tabular-nums\">\n                      {fmtCurrency(row.interestPaid)}\n                    </TableCell>\n                    <TableCell className=\"text-success text-success text-right tabular-nums\">\n                      {row.extraPaid > 0 ? `+${fmtCurrency(row.extraPaid)}` : '—'}\n                    </TableCell>\n                    <TableCell className=\"text-right font-semibold tabular-nums\">\n                      {fmtCurrency(row.totalPaid)}\n                    </TableCell>\n                    <TableCell className=\"text-right font-semibold tabular-nums\">\n                      {fmtCurrency(row.remainingBalance)}\n                    </TableCell>\n                  </TableRow>\n                ))}\n              </TableBody>\n              <TableFooter>\n                <TableRow>\n                  <TableCell className=\"font-bold\">\n                    Total ({displayedSchedule.length} {displayedSchedule.length === 1 ? 'Year' : 'Years'})\n                  </TableCell>\n                  <TableCell className=\"text-foreground text-right font-bold tabular-nums\">\n                    {fmtCurrency(displayedTotals.principal)}\n                  </TableCell>\n                  <TableCell className=\"text-warning text-warning text-right font-bold tabular-nums\">\n                    {fmtCurrency(displayedTotals.interest)}\n                  </TableCell>\n                  <TableCell className=\"text-success text-success text-right font-bold tabular-nums\">\n                    {fmtCurrency(displayedTotals.extra)}\n                  </TableCell>\n                  <TableCell className=\"text-right font-bold tabular-nums\">\n                    {fmtCurrency(displayedTotals.total)}\n                  </TableCell>\n                  <TableCell className=\"text-right font-bold tabular-nums\">\n                    {fmtCurrency(displayedSchedule[displayedSchedule.length - 1]?.remainingBalance ?? 0)}\n                  </TableCell>\n                </TableRow>\n              </TableFooter>\n            </Table>\n          </div>\n        </CardContent>\n      </Card>\n    </div>\n  )\n}\n\nexport default LoanCalculator\n",
      "type": "registry:block",
      "target": "~/components/blocks/LoanCalculator.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/input.json",
    "https://uipkge.dev/r/react/separator.json",
    "https://uipkge.dev/r/react/slider.json",
    "https://uipkge.dev/r/react/table.json",
    "https://uipkge.dev/r/react/tabs.json"
  ],
  "description": "Interactive mortgage and commercial loan amortization schedule calculator with dynamic sliders, loan type presets, extra payment impact simulation, principal vs interest visual ratio bar, and year-by-year amortization breakdown table.",
  "categories": [
    "finance",
    "billing"
  ]
}