{"id":334,"date":"2026-07-13T14:57:58","date_gmt":"2026-07-13T14:57:58","guid":{"rendered":"https:\/\/mathpk.com\/?page_id=334"},"modified":"2026-07-13T18:13:46","modified_gmt":"2026-07-13T18:13:46","slug":"test-page","status":"publish","type":"page","link":"https:\/\/mathpk.com\/?page_id=334","title":{"rendered":"Test Page"},"content":{"rendered":"\n<meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\">\n    <title>Functions | Complete MCQ Quiz (150 Questions)<\/title>\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/mathjax@3\/es5\/tex-chtml.js\" id=\"MathJax-script\" async=\"\"><\/script>\n    <style>\n        * { margin: 0; padding: 0; box-sizing: border-box; }\n        body {\n            background: linear-gradient(135deg, #eef2f5 0%, #d4e0e8 100%);\n            font-family: 'Segoe UI', 'Roboto', 'Poppins', sans-serif;\n            min-height: 100vh;\n            padding: 1rem;\n        }\n        .main-container { display: flex; gap: 1rem; max-width: 1600px; margin: 0 auto; height: calc(100vh - 2rem); }\n        .navigator-panel { width: 460px; background: white; border-radius: 1.2rem; box-shadow: 0 10px 30px -12px rgba(0,0,0,0.25); overflow: hidden; display: flex; flex-direction: column; height: 100%; }\n        .navigator-header { background: #1e3a4d; color: white; padding: 0.8rem; text-align: center; flex-shrink: 0; }\n        .navigator-header h3 { font-size: 1.6rem; margin-bottom: 0.3rem; font-weight: 800; }\n        .stats-summary { font-size: 2.9rem; background: #2c6e8f; display: inline-block; padding: 0.2rem 0.8rem; border-radius: 20px; font-weight: 500; }\n        .question-grid { display: grid; grid-template-columns: repeat(10, 1fr); gap: 0.25rem; padding: 0.6rem; flex: 1; overflow-y: auto; align-content: start; }\n        .q-link { display: flex; align-items: center; justify-content: center; width: 100%; aspect-ratio: 1; background: #eef3fc; border: 1px solid #cbdde8; border-radius: 5px; font-size: 0.65rem; font-weight: 700; cursor: pointer; transition: all 0.2s; color: #1f5e6e; }\n        .q-link:hover { background: #cde3f0; transform: scale(1.02); }\n        .q-link.attempted { background: #d4edda; border-color: #28a745; color: #155724; }\n        .q-link.unattempted { background: #f8f9fa; border-color: #dee2e6; color: #6c757d; }\n        .q-link.current { background: #ffc107; border-color: #ff9800; color: #333; font-weight: 800; box-shadow: 0 0 0 2px #ffc107, 0 0 0 3px #ff9800; }\n        .q-link.incorrect-final { background: #f8d7da; border-color: #dc3545; color: #721c24; }\n        .legend { display: flex; justify-content: space-around; padding: 0.4rem; background: #f8fafd; border-top: 1px solid #e2edf2; font-size: 0.6rem; flex-shrink: 0; color: #000000; font-weight: 600; }\n        .legend span { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 3px; }\n        .quiz-wrapper { flex: 1; background: white; border-radius: 1.2rem; box-shadow: 0 10px 30px -12px rgba(0,0,0,0.25); overflow: hidden; display: flex; flex-direction: column; height: 100%; }\n        .quiz-header { background: #1e3a4d; padding: 0.7rem 1rem; color: white; flex-shrink: 0; }\n        .stats-row { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; }\n        .q-counter { background: #2c6e8f; padding: 5px 12px; border-radius: 40px; font-weight: 700; font-size: 0.9rem; }\n        .score-area { background: #28a745; color: white; padding: 5px 12px; border-radius: 40px; font-weight: 700; font-size: 0.9rem; }\n        .clock-wrapper { display: flex; align-items: center; gap: 0.3rem; background: #1a4a5f; padding: 0.1rem 0.2rem 0.1rem 0.6rem; border-radius: 40px; }\n        .clock-container { display: flex; align-items: center; gap: 0.3rem; font-size: 0.9rem; font-weight: 600; }\n        .clock-display { font-family: 'Courier New', monospace; font-size: 0.9rem; font-weight: 700; }\n        .clock-start-btn, .clock-stop-btn { background: #28a745; color: white; border: none; padding: 0.15rem 0.5rem; border-radius: 30px; font-weight: 700; font-size: 0.65rem; cursor: pointer; }\n        .clock-stop-btn { background: #dc3545; }\n        .progress-bar-container { margin-top: 6px; background: #2e5a6e; border-radius: 30px; height: 4px; }\n        .progress-fill { background: #ffc107; width: 0%; height: 4px; border-radius: 30px; transition: width 0.3s ease; }\n        .quiz-body { padding: 0.8rem; flex: 1; overflow-y: auto; }\n        .question-text { font-size: 1.2rem; font-weight: 600; color: #0c2e3a; line-height: 1.35; margin: 0.3rem 0 0.8rem 0; }\n        .question-text mjx-container { font-size: 1.2rem !important; }\n        .options-area { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.6rem 0; }\n        .option-card { background: #fbfdff; border: 2px solid #e2edf2; border-radius: 0.7rem; padding: 0.5rem 0.8rem; display: flex; align-items: center; cursor: pointer; transition: all 0.2s; }\n        .option-card:hover { background: #eff6fc; border-color: #84a7c9; transform: translateX(4px); }\n        .option-card.selected { background: #e1f0fa; border-color: #1f6e8c; }\n        .opt-letter { font-weight: 800; font-size: 1rem; width: 2rem; color: #0f4c5f; }\n        .opt-text { font-size: 0.95rem; font-weight: 500; flex: 1; line-height: 1.3; }\n        .opt-text mjx-container { font-size: 0.95rem !important; }\n        .feedback-msg { border-radius: 0.7rem; padding: 0.6rem 0.8rem; margin: 0.5rem 0; display: none; gap: 8px; align-items: flex-start; }\n        .feedback-msg.show { display: flex; }\n        .feedback-msg.correct-feedback { background: #d4edda; border-left: 4px solid #28a745; }\n        .feedback-msg.wrong-feedback { background: #fff3cd; border-left: 4px solid #ffc107; }\n        .feedback-icon { font-size: 1rem; }\n        .feedback-content { flex: 1; font-size: 0.85rem; font-weight: 500; line-height: 1.35; color: #1a1a1a; }\n        .nav-row { display: flex; justify-content: space-between; margin-top: 0.6rem; gap: 0.6rem; }\n        button { background: #eef3fc; border: none; padding: 0.4rem 1rem; border-radius: 2rem; font-weight: 700; font-size: 0.8rem; cursor: pointer; transition: 0.2s; color: #1f5e6e; }\n        button.primary-btn { background: #1e3a4d; color: white; }\n        button.primary-btn:hover { background: #144d62; }\n        .reset-btn { background: #ffe1cf; color: #b13e1a; }\n        @media (max-width: 900px) {\n            .main-container { flex-direction: column; height: auto; }\n            .navigator-panel { width: 100%; height: auto; max-height: 260px; }\n            .question-grid { overflow-y: auto; max-height: 200px; }\n            .question-text { font-size: 1.05rem; }\n            .opt-text { font-size: 0.9rem; }\n            .opt-letter { font-size: 0.95rem; }\n        }\n    <\/style>\n\n\n\n<div class=\"main-container\">\n    <div class=\"navigator-panel\">\n        <div class=\"navigator-header\">\n            <h3>\ud83d\udcd6 Question Navigator <\/h3>\n            <div class=\"stats-summary\" id=\"attemptedStats\">0 \/ 150 Attempted<\/div>\n        <\/div>\n        <div class=\"question-grid\" id=\"questionGrid\"><\/div>\n        <div class=\"legend\">\n            <span style=\"background: #d4edda; border: 1px solid #28a745;\"><\/span> Attempted\n            <span style=\"background: #f8f9fa; border: 1px solid #dee2e6;\"><\/span> Unattempted\n            <span style=\"background: #ffc107;\"><\/span> Current\n            <span style=\"background: #f8d7da; border: 1px solid #dc3545;\"><\/span> Incorrect (Final)\n        <\/div>\n    <\/div>\n\n    <div class=\"quiz-wrapper\">\n        <div class=\"quiz-header\">\n            <div class=\"stats-row\">\n                <span class=\"q-counter\" id=\"questionCounter\">Q1 of 150<\/span>\n                <div class=\"score-area\" id=\"scoreDisplay\">\u2b50 Score: 0 \/ 150<\/div>\n                <div class=\"clock-wrapper\">\n                    <div class=\"clock-container\"><span>\u23f1\ufe0f<\/span><span class=\"clock-display\" id=\"clockDisplay\">00:00:00<\/span><\/div>\n                    <button id=\"clockStartBtn\" class=\"clock-start-btn\">\u25b6\ufe0f Start<\/button>\n                    <button id=\"clockStopBtn\" class=\"clock-stop-btn\">\u23f9\ufe0f Stop<\/button>\n                <\/div>\n            <\/div>\n            <div class=\"progress-bar-container\"><div class=\"progress-fill\" id=\"progressFill\"><\/div><\/div>\n        <\/div>\n        <div class=\"quiz-body\">\n            <div class=\"question-text\" id=\"questionText\"><\/div>\n            <div class=\"options-area\" id=\"optionsContainer\"><\/div>\n            <div class=\"feedback-msg\" id=\"feedbackMsg\">\n                <span class=\"feedback-icon\">\ud83d\udcd8<\/span>\n                <div class=\"feedback-content\" id=\"feedbackText\"><\/div>\n            <\/div>\n            <div class=\"nav-row\">\n                <button id=\"prevBtn\">\u2190 Prev<\/button>\n                <button id=\"nextBtn\" class=\"primary-btn\">Next \u2192<\/button>\n                <button id=\"resetBtn\" class=\"reset-btn\">\u27f3 Reset<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n<\/div>\n\n<script>\n    \/\/ ==================== 150 MCQs on Functions ====================\n    const MCQ_SET = [\n        { text: \"If \\\\(f(x)=x^2+3x-2\\\\), which of the following statements correctly describes the nature of this function?\", options: [\"It is a one-to-one function for all real numbers\", \"It is an onto function for codomain \\\\(\\\\mathbb{R}\\\\)\", \"It is neither one-to-one nor onto for codomain \\\\(\\\\mathbb{R}\\\\)\", \"It is a bijective function for codomain \\\\(\\\\mathbb{R}\\\\)\"], correct: 2, explanation: \"Quadratic function is not one-to-one (fails horizontal line test) and not onto since range is limited.\" },\n        { text: \"The domain of \\\\(f(x)=\\\\sqrt{\\\\frac{x-3}{x^2-9}}\\\\) is:\", options: [\"\\\\([3,\\\\infty)\\\\)\", \"\\\\((3,\\\\infty)\\\\)\", \"\\\\([3,\\\\infty)\\\\setminus\\\\{3\\\\}\\\\)\", \"\\\\((3,\\\\infty)\\\\setminus\\\\{3\\\\}\\\\)\"], correct: 1, explanation: \"Denominator zero at x=\u00b13, and numerator requires x\u22653, so domain (3,\u221e).\" },\n        { text: \"If \\\\(f(x)=2x+5\\\\) and \\\\(g(x)=x^2-1\\\\), then \\\\((f\\\\circ g)(2)\\\\) equals:\", options: [\"11\", \"9\", \"13\", \"7\"], correct: 0, explanation: \"g(2)=4-1=3, f(3)=6+5=11.\" },\n        { text: \"Which of the following functions is NOT a one-to-one function on its natural domain?\", options: [\"\\\\(f(x)=x^3\\\\)\", \"\\\\(f(x)=e^x\\\\)\", \"\\\\(f(x)=x^2\\\\)\", \"\\\\(f(x)=\\\\ln x\\\\)\"], correct: 2, explanation: \"x\u00b2 is not one-to-one because f(2)=f(-2).\" },\n        { text: \"The range of \\\\(f(x)=|x-2|+3\\\\) is:\", options: [\"\\\\((-\\\\infty,\\\\infty)\\\\)\", \"\\\\([3,\\\\infty)\\\\)\", \"\\\\([0,\\\\infty)\\\\)\", \"\\\\((2,\\\\infty)\\\\)\"], correct: 1, explanation: \"Absolute value minimum is 0, so minimum of f is 3.\" },\n        { text: \"For a function to have an inverse that is also a function, it must be:\", options: [\"Surjective only\", \"Injective only\", \"Bijective\", \"Into function\"], correct: 2, explanation: \"Function must be bijective (both one-to-one and onto) to have an inverse function.\" },\n        { text: \"If \\\\(f(x)=\\\\frac{2x-1}{x+3}, x\\\\neq -3\\\\), then \\\\(f^{-1}(x)\\\\) is:\", options: [\"\\\\(\\\\frac{x+3}{2x-1}\\\\)\", \"\\\\(\\\\frac{3x+1}{2-x}\\\\)\", \"\\\\(\\\\frac{3x-1}{2-x}\\\\)\", \"\\\\(\\\\frac{3x+1}{x-2}\\\\)\"], correct: 1, explanation: \"Solve y=(2x-1)\/(x+3) for x: y(x+3)=2x-1 \u2192 xy+3y=2x-1 \u2192 3y+1=2x-xy \u2192 x=(3y+1)\/(2-y).\" },\n        { text: \"The graph of \\\\(y=-f(x)\\\\) is obtained by:\", options: [\"Shifting the graph of \\\\(f(x)\\\\) vertically downward\", \"Reflecting the graph of \\\\(f(x)\\\\) across the x-axis\", \"Reflecting the graph of \\\\(f(x)\\\\) across the y-axis\", \"Shifting the graph of \\\\(f(x)\\\\) horizontally\"], correct: 1, explanation: \"Multiplying by -1 reflects across x-axis.\" },\n        { text: \"Which of the following is a transcendental function?\", options: [\"\\\\(f(x)=x^3-2x+1\\\\)\", \"\\\\(f(x)=\\\\frac{x^2+1}{x-2}\\\\)\", \"\\\\(f(x)=2^x\\\\)\", \"\\\\(f(x)=\\\\sqrt{x^2+4}\\\\)\"], correct: 2, explanation: \"Exponential function 2^x is transcendental (non-algebraic).\" },\n        { text: \"The domain of \\\\(f(x)=\\\\ln(5-x)\\\\) is:\", options: [\"\\\\((5,\\\\infty)\\\\)\", \"\\\\((-\\\\infty,5)\\\\)\", \"\\\\((-\\\\infty,5]\\\\)\", \"\\\\((0,5)\\\\)\"], correct: 1, explanation: \"ln argument >0 \u21d2 5-x>0 \u21d2 x<5.\" },\n        { text: \"If the horizontal line test fails for a function, the function:\", options: [\"Is surjective\", \"Is not one-to-one\", \"Has no inverse at all\", \"Is bijective\"], correct: 1, explanation: \"Horizontal line test checks injectivity (one-to-one).\" },\n        { text: \"The graph of \\\\(f(x)=2^x\\\\) and \\\\(g(x)=\\\\log_2 x\\\\) are symmetric with respect to:\", options: [\"The x-axis\", \"The y-axis\", \"The line \\\\(y=x\\\\)\", \"The origin\"], correct: 2, explanation: \"Exponential and logarithmic functions are inverses, symmetric about y=x.\" },\n        { text: \"The range of \\\\(f(x)=\\\\sin x\\\\) for \\\\(x\\\\in\\\\mathbb{R}\\\\) is:\", options: [\"\\\\([0,1]\\\\)\", \"\\\\((-1,1)\\\\)\", \"\\\\([-1,1]\\\\)\", \"\\\\([-\\\\pi,\\\\pi]\\\\)\"], correct: 2, explanation: \"Sine function ranges from -1 to 1 inclusive.\" },\n        { text: \"Which transformation maps \\\\(f(x)\\\\) to \\\\(f(2x)\\\\)?\", options: [\"Horizontal stretch by factor 2\", \"Horizontal compression by factor \\\\(\\\\frac12\\\\)\", \"Vertical stretch by factor 2\", \"Vertical compression by factor \\\\(\\\\frac12\\\\)\"], correct: 1, explanation: \"f(2x) compresses horizontally by factor 1\/2.\" },\n        { text: \"If \\\\(f(x)=x^2\\\\) and \\\\(g(x)=\\\\sqrt{x}\\\\), then \\\\((f\\\\circ g)(x)\\\\) for \\\\(x\\\\ge 0\\\\) is:\", options: [\"\\\\(x\\\\)\", \"\\\\(|x|\\\\)\", \"\\\\(x^2\\\\)\", \"\\\\(\\\\sqrt{x^2}\\\\)\"], correct: 0, explanation: \"f(g(x)) = (\u221ax)\u00b2 = x for x\u22650.\" },\n        { text: \"A function \\\\(f:A\\\\to B\\\\) is called onto (surjective) if:\", options: [\"Every element of A has a unique image in B\", \"Every element of B is the image of some element of A\", \"The function is one-to-one\", \"The domain equals the range\"], correct: 1, explanation: \"Definition of surjective: every element in codomain has a preimage.\" },\n        { text: \"The period of \\\\(y=\\\\tan x\\\\) is:\", options: [\"\\\\(\\\\pi\\\\)\", \"\\\\(2\\\\pi\\\\)\", \"\\\\(\\\\frac{\\\\pi}{2}\\\\)\", \"\\\\(\\\\pi\/4\\\\)\"], correct: 0, explanation: \"Tangent function has period \u03c0.\" },\n        { text: \"For \\\\(f(x)=x^2-4x+7\\\\), the vertex of the parabola is at:\", options: [\"\\\\((2,3)\\\\)\", \"\\\\((-2,19)\\\\)\", \"\\\\((2,-3)\\\\)\", \"\\\\((4,7)\\\\)\"], correct: 0, explanation: \"Vertex x=-b\/(2a)=4\/2=2, f(2)=4-8+7=3.\" },\n        { text: \"If \\\\(f(x)=\\\\frac{1}{x}\\\\), then \\\\(f^{-1}(x)\\\\) is:\", options: [\"\\\\(x\\\\)\", \"\\\\(-x\\\\)\", \"\\\\(\\\\frac{1}{x}\\\\)\", \"\\\\(-\\\\frac{1}{x}\\\\)\"], correct: 2, explanation: \"f is its own inverse (involution).\" },\n        { text: \"The domain of \\\\(f(x)=\\\\frac{\\\\ln(x-1)}{x-2}\\\\) is:\", options: [\"\\\\((1,\\\\infty)\\\\)\", \"\\\\((1,\\\\infty)\\\\setminus\\\\{2\\\\}\\\\)\", \"\\\\([1,\\\\infty)\\\\setminus\\\\{2\\\\}\\\\)\", \"\\\\((1,2)\\\\cup(2,\\\\infty)\\\\)\"], correct: 3, explanation: \"ln(x-1) requires x>1, denominator zero at x=2.\" },\n        { text: \"Which of the following statements about \\\\(y=\\\\arcsin x\\\\) is TRUE?\", options: [\"Domain is \\\\([-1,1]\\\\), range is \\\\([-\\\\pi\/2,\\\\pi\/2]\\\\)\", \"Domain is \\\\(\\\\mathbb{R}\\\\), range is \\\\([-\\\\pi\/2,\\\\pi\/2]\\\\)\", \"Domain is \\\\([-1,1]\\\\), range is \\\\([0,\\\\pi]\\\\)\", \"Domain is \\\\(\\\\mathbb{R}\\\\), range is \\\\([0,\\\\pi]\\\\)\"], correct: 0, explanation: \"Arcsin has domain [-1,1] and range [-\u03c0\/2,\u03c0\/2].\" },\n        { text: \"If the graph of \\\\(f(x)\\\\) passes through the point \\\\((2,5)\\\\), then the graph of \\\\(f^{-1}(x)\\\\) passes through:\", options: [\"\\\\((2,5)\\\\)\", \"\\\\((5,2)\\\\)\", \"\\\\((-2,-5)\\\\)\", \"\\\\((-5,-2)\\\\)\"], correct: 1, explanation: \"Inverse swaps coordinates.\" },\n        { text: \"The function \\\\(f(x)=|x|\\\\) is:\", options: [\"One-to-one on \\\\(\\\\mathbb{R}\\\\)\", \"Even function\", \"Odd function\", \"Neither even nor odd\"], correct: 1, explanation: \"|x| is even because f(-x)=f(x).\" },\n        { text: \"If \\\\(f(x)=e^x\\\\), then the range of \\\\(f(x)\\\\) is:\", options: [\"\\\\(\\\\mathbb{R}\\\\)\", \"\\\\((0,\\\\infty)\\\\)\", \"\\\\([0,\\\\infty)\\\\)\", \"\\\\((-\\\\infty,\\\\infty)\\\\)\"], correct: 1, explanation: \"Exponential function outputs positive numbers.\" },\n        { text: \"The horizontal asymptote of \\\\(y=\\\\frac{3x^2+2}{x^2-1}\\\\) is:\", options: [\"\\\\(y=0\\\\)\", \"\\\\(y=3\\\\)\", \"\\\\(y=2\\\\)\", \"No horizontal asymptote\"], correct: 1, explanation: \"Degree of numerator and denominator equal, ratio of leading coefficients = 3.\" },\n        { text: \"For a bijective function \\\\(f\\\\), which property holds?\", options: [\"\\\\(f^{-1}(f(x))=x\\\\) for all x in domain\", \"\\\\(f(f^{-1}(x))=x\\\\) for all x in codomain\", \"Both A and B\", \"Neither A nor B\"], correct: 2, explanation: \"Both cancellation properties hold for bijective functions.\" },\n        { text: \"The graph of \\\\(y=\\\\cos x\\\\) is symmetric about:\", options: [\"The origin\", \"The x-axis\", \"The y-axis\", \"The line \\\\(y=x\\\\)\"], correct: 2, explanation: \"Cosine is even: cos(-x)=cos x, symmetric about y-axis.\" },\n        { text: \"If \\\\(f(x)=3x+2\\\\) and \\\\(g(x)=x^2\\\\), then \\\\((g\\\\circ f)(x)\\\\) equals:\", options: [\"\\\\(9x^2+12x+4\\\\)\", \"\\\\(3x^2+2\\\\)\", \"\\\\(9x^2+4\\\\)\", \"\\\\(3x^2+2x\\\\)\"], correct: 0, explanation: \"g(f(x)) = (3x+2)\u00b2 = 9x\u00b2+12x+4.\" },\n        { text: \"The graph of \\\\(y=f(x)+2\\\\) is obtained by:\", options: [\"Shifting the graph of \\\\(f(x)\\\\) 2 units right\", \"Shifting the graph of \\\\(f(x)\\\\) 2 units left\", \"Shifting the graph of \\\\(f(x)\\\\) 2 units up\", \"Shifting the graph of \\\\(f(x)\\\\) 2 units down\"], correct: 2, explanation: \"Adding constant shifts graph vertically upward.\" },\n        { text: \"Which of the following is a power function?\", options: [\"\\\\(f(x)=2^x\\\\)\", \"\\\\(f(x)=x^{3\/2}\\\\)\", \"\\\\(f(x)=\\\\log_3 x\\\\)\", \"\\\\(f(x)=\\\\sin x\\\\)\"], correct: 1, explanation: \"Power functions have form x^r.\" },\n        { text: \"The domain of \\\\(f(x)=\\\\sqrt{9-x^2}\\\\) is:\", options: [\"\\\\((-\\\\infty,\\\\infty)\\\\)\", \"\\\\([-3,3]\\\\)\", \"\\\\((-\\\\infty,-3]\\\\cup[3,\\\\infty)\\\\)\", \"\\\\((-3,3)\\\\)\"], correct: 1, explanation: \"Require 9-x\u00b2 \u2265 0 \u21d2 x\u00b2 \u2264 9 \u21d2 |x| \u2264 3.\" },\n        { text: \"If \\\\(f\\\\) is a one-to-one function and \\\\(f(3)=7\\\\), then \\\\(f^{-1}(7)\\\\) equals:\", options: [\"\\\\(\\\\frac17\\\\)\", \"3\", \"-3\", \"\\\\(\\\\frac13\\\\)\"], correct: 1, explanation: \"Inverse swaps input and output.\" },\n        { text: \"Which transformation maps \\\\(f(x)\\\\) to \\\\(f(x-3)\\\\)?\", options: [\"Shift right by 3 units\", \"Shift left by 3 units\", \"Shift up by 3 units\", \"Shift down by 3 units\"], correct: 0, explanation: \"f(x-3) shifts graph right by 3.\" },\n        { text: \"The function \\\\(f(x)=\\\\tan x\\\\) is undefined when:\", options: [\"\\\\(x=n\\\\pi\\\\)\", \"\\\\(x=\\\\frac{\\\\pi}{2}+n\\\\pi\\\\)\", \"\\\\(x=2n\\\\pi\\\\)\", \"\\\\(x=\\\\frac{\\\\pi}{2}+2n\\\\pi\\\\)\"], correct: 1, explanation: \"tan x = sin x\/cos x, undefined when cos x=0.\" },\n        { text: \"If \\\\(f(x)=\\\\sqrt{x}\\\\) and \\\\(g(x)=x+1\\\\), then the domain of \\\\((f\\\\circ g)(x)\\\\) is:\", options: [\"\\\\([0,\\\\infty)\\\\)\", \"\\\\([-1,\\\\infty)\\\\)\", \"\\\\([1,\\\\infty)\\\\)\", \"\\\\(\\\\mathbb{R}\\\\)\"], correct: 1, explanation: \"f(g(x)) = \u221a(x+1) requires x+1 \u2265 0 \u21d2 x \u2265 -1.\" },\n        { text: \"The vertical asymptote(s) of \\\\(y=\\\\frac{x+1}{x^2-4}\\\\) occur at:\", options: [\"\\\\(x=2\\\\) only\", \"\\\\(x=-2\\\\) only\", \"\\\\(x=\\\\pm 2\\\\)\", \"\\\\(x=0\\\\)\"], correct: 2, explanation: \"Denominator zero at x=\u00b12, numerator not zero there.\" },\n        { text: \"The range of \\\\(f(x)=e^{-x^2}\\\\) is:\", options: [\"\\\\((0,1]\\\\)\", \"\\\\([0,1]\\\\)\", \"\\\\((0,\\\\infty)\\\\)\", \"\\\\((-\\\\infty,1]\\\\)\"], correct: 0, explanation: \"Exponential decays from 1 to 0, never reaching 0.\" },\n        { text: \"Which of the following is an even function?\", options: [\"\\\\(f(x)=x^3-x\\\\)\", \"\\\\(f(x)=\\\\cos x\\\\)\", \"\\\\(f(x)=e^x\\\\)\", \"\\\\(f(x)=\\\\ln x\\\\)\"], correct: 1, explanation: \"Cosine is even: cos(-x)=cos x.\" },\n        { text: \"If \\\\(f(x)=x^2+1\\\\), which transformation produces \\\\(g(x)=(x-2)^2+1\\\\)?\", options: [\"Shift right 2 units\", \"Shift left 2 units\", \"Shift down 2 units\", \"Shift up 2 units\"], correct: 0, explanation: \"Replacing x with x-2 shifts right.\" },\n        { text: \"\\\\(\\\\log_2 16 + \\\\log_3 81\\\\) equals:\", options: [\"8\", \"12\", \"4\", \"16\"], correct: 0, explanation: \"log\u208216=4, log\u208381=4, sum=8.\" },\n        { text: \"The graph of a function and its inverse are reflections across:\", options: [\"x-axis\", \"y-axis\", \"Line \\\\(y=x\\\\)\", \"Origin\"], correct: 2, explanation: \"Inverse functions are symmetric about y=x.\" },\n        { text: \"The domain of \\\\(f(x)=\\\\sqrt{x^2-4}\\\\) is:\", options: [\"\\\\([-2,2]\\\\)\", \"\\\\((-\\\\infty,-2]\\\\cup[2,\\\\infty)\\\\)\", \"\\\\(\\\\mathbb{R}\\\\)\", \"\\\\([-2,\\\\infty)\\\\)\"], correct: 1, explanation: \"x\u00b2-4 \u2265 0 \u21d2 |x| \u2265 2.\" },\n        { text: \"If \\\\(f(x)=x^3\\\\), then the inverse function \\\\(f^{-1}(x)\\\\) is:\", options: [\"\\\\(\\\\sqrt[3]{x}\\\\)\", \"\\\\(\\\\frac{1}{x^3}\\\\)\", \"\\\\(\\\\pm\\\\sqrt[3]{x}\\\\)\", \"\\\\(x^{1\/3}\\\\)\"], correct: 3, explanation: \"Cube root is the inverse of cube function.\" },\n        { text: \"The range of \\\\(f(x)=2\\\\sin x+3\\\\) is:\", options: [\"\\\\([2,4]\\\\)\", \"\\\\([1,5]\\\\)\", \"\\\\([3,5]\\\\)\", \"\\\\([-2,2]\\\\)\"], correct: 1, explanation: \"sin x \u2208 [-1,1], so 2sin x+3 \u2208 [1,5].\" },\n        { text: \"Which statement about \\\\(y=\\\\arccos x\\\\) is FALSE?\", options: [\"Domain is \\\\([-1,1]\\\\)\", \"Range is \\\\([0,\\\\pi]\\\\)\", \"\\\\(\\\\arccos(0)=\\\\frac{\\\\pi}{2}\\\\)\", \"\\\\(\\\\arccos x\\\\) is an odd function\"], correct: 3, explanation: \"Arccos is neither even nor odd; arccos(-x)=\u03c0-arccos x.\" },\n        { text: \"If \\\\(f(x)=\\\\ln x\\\\) and \\\\(g(x)=e^x\\\\), then \\\\(f(g(x))\\\\) equals:\", options: [\"\\\\(x\\\\)\", \"\\\\(e^x\\\\)\", \"\\\\(\\\\ln(e^x)\\\\)\", \"Both A and C\"], correct: 3, explanation: \"ln(e^x)=x for all real x.\" },\n        { text: \"The graph of \\\\(y=\\\\csc x\\\\) has vertical asymptotes at:\", options: [\"\\\\(x=n\\\\pi\\\\)\", \"\\\\(x=\\\\frac{\\\\pi}{2}+n\\\\pi\\\\)\", \"\\\\(x=2n\\\\pi\\\\)\", \"\\\\(x=n\\\\pi\/2\\\\)\"], correct: 0, explanation: \"csc x = 1\/sin x, undefined when sin x=0 at x=n\u03c0.\" },\n        { text: \"A function is said to be one-to-one (injective) if:\", options: [\"Every element in codomain has a preimage\", \"Different inputs produce different outputs\", \"The function is continuous\", \"The function has an inverse\"], correct: 1, explanation: \"Injective means f(a)=f(b) \u21d2 a=b.\" },\n        { text: \"The vertical scaling of \\\\(f(x)\\\\) by a factor of 3 produces:\", options: [\"\\\\(3f(x)\\\\)\", \"\\\\(f(3x)\\\\)\", \"\\\\(f(x\/3)\\\\)\", \"\\\\(f(x)+3\\\\)\"], correct: 0, explanation: \"Vertical scaling multiplies output by factor.\" },\n        { text: \"The equation \\\\(\\\\log_2 x = 3\\\\) is equivalent to:\", options: [\"\\\\(x=2^3\\\\)\", \"\\\\(x=3^2\\\\)\", \"\\\\(2^x=3\\\\)\", \"\\\\(x^2=3\\\\)\"], correct: 0, explanation: \"Logarithmic form to exponential: 2\u00b3 = x.\" },\n        { text: \"The range of \\\\(f(x)=|x|+1\\\\) is:\", options: [\"\\\\([1,\\\\infty)\\\\)\", \"\\\\((-\\\\infty,\\\\infty)\\\\)\", \"\\\\([0,\\\\infty)\\\\)\", \"\\\\((1,\\\\infty)\\\\)\"], correct: 0, explanation: \"|x|\u22650, so |x|+1\u22651.\" },\n        { text: \"If \\\\(f(x)=2x-3\\\\), then \\\\(f^{-1}(5)\\\\) equals:\", options: [\"7\", \"4\", \"1\", \"13\"], correct: 1, explanation: \"Solve 2x-3=5 \u21d2 2x=8 \u21d2 x=4.\" },\n        { text: \"The horizontal asymptote of \\\\(y=\\\\frac{2x}{x^2+1}\\\\) is:\", options: [\"\\\\(y=0\\\\)\", \"\\\\(y=2\\\\)\", \"\\\\(y=1\\\\)\", \"No horizontal asymptote\"], correct: 0, explanation: \"Degree denominator > numerator, horizontal asymptote y=0.\" },\n        { text: \"\\\\(\\\\ln e^4\\\\) equals:\", options: [\"4\", \"\\\\(e^4\\\\)\", \"1\", \"0\"], correct: 0, explanation: \"ln(e^4)=4.\" },\n        { text: \"If the point \\\\((a,b)\\\\) lies on the graph of \\\\(y=f(x)\\\\), then which point lies on the graph of \\\\(y=f(x-h)\\\\)?\", options: [\"\\\\((a+h,b)\\\\)\", \"\\\\((a-h,b)\\\\)\", \"\\\\((a,b+h)\\\\)\", \"\\\\((a,b-h)\\\\)\"], correct: 0, explanation: \"Shift right by h: x-coordinate increases by h.\" },\n        { text: \"The period of \\\\(y=\\\\sin(2x)\\\\) is:\", options: [\"\\\\(\\\\pi\\\\)\", \"\\\\(2\\\\pi\\\\)\", \"\\\\(\\\\frac{\\\\pi}{2}\\\\)\", \"\\\\(4\\\\pi\\\\)\"], correct: 0, explanation: \"Period = 2\u03c0\/2 = \u03c0.\" },\n        { text: \"Which of the following is a rational function?\", options: [\"\\\\(f(x)=\\\\sqrt{x}\\\\)\", \"\\\\(f(x)=\\\\frac{x^2+1}{x-2}\\\\)\", \"\\\\(f(x)=3^x\\\\)\", \"\\\\(f(x)=\\\\sin x\\\\)\"], correct: 1, explanation: \"Rational function is ratio of polynomials.\" },\n        { text: \"The domain of \\\\(f(x)=\\\\arcsin(2x)\\\\) is:\", options: [\"\\\\([-1,1]\\\\)\", \"\\\\([-\\\\frac12,\\\\frac12]\\\\)\", \"\\\\([-2,2]\\\\)\", \"\\\\(\\\\mathbb{R}\\\\)\"], correct: 1, explanation: \"Arcsin domain requires |2x|\u22641 \u21d2 |x|\u22641\/2.\" },\n        { text: \"If \\\\(f(x)=x^2+2x\\\\) and \\\\(g(x)=x+1\\\\), then \\\\(f(g(x))\\\\) simplifies to:\", options: [\"\\\\(x^2+4x+3\\\\)\", \"\\\\(x^2+2x+1\\\\)\", \"\\\\(x^2+2x+3\\\\)\", \"\\\\(x^2+3x+3\\\\)\"], correct: 0, explanation: \"(x+1)\u00b2+2(x+1) = x\u00b2+2x+1+2x+2 = x\u00b2+4x+3.\" },\n        { text: \"The horizontal compression of \\\\(f(x)\\\\) by a factor of \\\\(\\\\frac12\\\\) is represented by:\", options: [\"\\\\(2f(x)\\\\)\", \"\\\\(f(2x)\\\\)\", \"\\\\(\\\\frac12 f(x)\\\\)\", \"\\\\(f(x\/2)\\\\)\"], correct: 1, explanation: \"f(2x) compresses horizontally.\" },\n        { text: \"The function \\\\(f(x)=x^3-x\\\\) is:\", options: [\"Even\", \"Odd\", \"Neither even nor odd\", \"Both even and odd\"], correct: 1, explanation: \"f(-x)= -x\u00b3 + x = -(x\u00b3-x) = -f(x), so odd.\" },\n        { text: \"The range of \\\\(f(x)=\\\\frac{1}{x^2+1}\\\\) is:\", options: [\"\\\\((0,1]\\\\)\", \"\\\\([0,1]\\\\)\", \"\\\\((0,\\\\infty)\\\\)\", \"\\\\((-\\\\infty,1]\\\\)\"], correct: 0, explanation: \"x\u00b2+1\u22651, so 1\/(x\u00b2+1) \u2208 (0,1].\" },\n        { text: \"If \\\\(f(x)=\\\\frac{x+2}{x-3}\\\\), then the vertical asymptote is at:\", options: [\"\\\\(x=-2\\\\)\", \"\\\\(x=3\\\\)\", \"\\\\(x=-3\\\\)\", \"\\\\(x=2\\\\)\"], correct: 1, explanation: \"Denominator zero at x=3.\" },\n        { text: \"\\\\(\\\\log_5 125 + \\\\log_2 8\\\\) equals:\", options: [\"6\", \"3\", \"9\", \"5\"], correct: 0, explanation: \"log\u2085125=3, log\u20828=3, sum=6.\" },\n        { text: \"The graph of \\\\(y=f(x)+k\\\\) is obtained by shifting the graph of \\\\(f(x)\\\\):\", options: [\"Vertically if \\\\(k>0\\\\)\", \"Horizontally\", \"Vertically regardless of sign\", \"Horizontally if \\\\(k<0\\\\)\"], correct: 2, explanation: \"Adding constant always shifts vertically.\" },\n        { text: \"Which of the following functions has domain \\\\(\\\\mathbb{R}\\\\)?\", options: [\"\\\\(f(x)=\\\\ln x\\\\)\", \"\\\\(f(x)=\\\\sqrt{x}\\\\)\", \"\\\\(f(x)=\\\\tan x\\\\)\", \"\\\\(f(x)=x^3-2x\\\\)\"], correct: 3, explanation: \"Polynomials have domain all reals.\" },\n        { text: \"If \\\\(f(x)=\\\\frac{x}{x+1}\\\\), then \\\\(f^{-1}(x)\\\\) is:\", options: [\"\\\\(\\\\frac{x}{1-x}\\\\)\", \"\\\\(\\\\frac{x}{x-1}\\\\)\", \"\\\\(\\\\frac{1-x}{x}\\\\)\", \"\\\\(\\\\frac{1+x}{x}\\\\)\"], correct: 0, explanation: \"Solve y=x\/(x+1) \u21d2 y(x+1)=x \u21d2 xy+y=x \u21d2 y=x-xy \u21d2 x=y\/(1-y).\" },\n        { text: \"The amplitude of \\\\(y=4\\\\cos(3x)\\\\) is:\", options: [\"4\", \"3\", \"\\\\(\\\\frac43\\\\)\", \"1\"], correct: 0, explanation: \"Amplitude is coefficient 4.\" },\n        { text: \"The graph of \\\\(y=\\\\sec x\\\\) has vertical asymptotes where:\", options: [\"\\\\(\\\\cos x=0\\\\)\", \"\\\\(\\\\sin x=0\\\\)\", \"\\\\(\\\\cos x=1\\\\)\", \"\\\\(\\\\sin x=1\\\\)\"], correct: 0, explanation: \"sec x = 1\/cos x, undefined when cos x=0.\" },\n        { text: \"If \\\\(f(x)=e^{2x}\\\\), then \\\\(f^{-1}(x)\\\\) equals:\", options: [\"\\\\(\\\\frac12\\\\ln x\\\\)\", \"\\\\(2\\\\ln x\\\\)\", \"\\\\(e^{x\/2}\\\\)\", \"\\\\(\\\\ln(2x)\\\\)\"], correct: 0, explanation: \"Solve y=e^{2x} \u21d2 ln y = 2x \u21d2 x = (1\/2)ln y.\" },\n        { text: \"The range of \\\\(f(x)=3^x\\\\) is:\", options: [\"\\\\(\\\\mathbb{R}\\\\)\", \"\\\\((0,\\\\infty)\\\\)\", \"\\\\([0,\\\\infty)\\\\)\", \"\\\\((1,\\\\infty)\\\\)\"], correct: 1, explanation: \"Exponential base >0 gives positive outputs.\" },\n        { text: \"The zeros of \\\\(f(x)=x^2-5x+6\\\\) are:\", options: [\"2 and 3\", \"-2 and -3\", \"1 and 6\", \"-1 and -6\"], correct: 0, explanation: \"Factor: (x-2)(x-3)=0 \u21d2 x=2,3.\" },\n        { text: \"Which transformation reflects \\\\(f(x)\\\\) across the y-axis?\", options: [\"\\\\(f(-x)\\\\)\", \"\\\\(-f(x)\\\\)\", \"\\\\(f(x)+1\\\\)\", \"\\\\(f(x-1)\\\\)\"], correct: 0, explanation: \"f(-x) reflects across y-axis.\" },\n        { text: \"\\\\(\\\\ln 1 + \\\\ln e\\\\) equals:\", options: [\"1\", \"0\", \"e\", \"2\"], correct: 0, explanation: \"ln1=0, ln e=1, sum=1.\" },\n        { text: \"The vertical asymptote(s) of \\\\(y=\\\\tan x\\\\) occur where:\", options: [\"\\\\(\\\\sin x=0\\\\)\", \"\\\\(\\\\cos x=0\\\\)\", \"\\\\(\\\\tan x=0\\\\)\", \"\\\\(\\\\cot x=0\\\\)\"], correct: 1, explanation: \"tan x = sin x\/cos x, undefined when cos x=0.\" },\n        { text: \"If \\\\(f(x)=2x+3\\\\) and \\\\(g(x)=4x-1\\\\), then \\\\(f(x)=g(x)\\\\) when \\\\(x\\\\) equals:\", options: [\"2\", \"-2\", \"1\", \"-1\"], correct: 0, explanation: \"2x+3=4x-1 \u21d2 4=2x \u21d2 x=2.\" },\n        { text: \"The domain of \\\\(f(x)=\\\\log_2(x^2-4)\\\\) is:\", options: [\"\\\\((-\\\\infty,-2)\\\\cup(2,\\\\infty)\\\\)\", \"\\\\((-2,2)\\\\)\", \"\\\\(\\\\mathbb{R}\\\\)\", \"\\\\([2,\\\\infty)\\\\)\"], correct: 0, explanation: \"x\u00b2-4>0 \u21d2 |x|>2.\" },\n        { text: \"The x-coordinate of the vertex of \\\\(f(x)=ax^2+bx+c\\\\) is:\", options: [\"\\\\(-\\\\frac{b}{2a}\\\\)\", \"\\\\(\\\\frac{b}{2a}\\\\)\", \"\\\\(-\\\\frac{b}{a}\\\\)\", \"\\\\(\\\\frac{b}{a}\\\\)\"], correct: 0, explanation: \"Vertex formula x = -b\/(2a).\" },\n        { text: \"If \\\\(f(x)=x^2\\\\) for \\\\(x\\\\ge 0\\\\), then \\\\(f^{-1}(x)\\\\) is:\", options: [\"\\\\(\\\\sqrt{x}\\\\)\", \"\\\\(-\\\\sqrt{x}\\\\)\", \"\\\\(\\\\pm\\\\sqrt{x}\\\\)\", \"\\\\(x^2\\\\)\"], correct: 0, explanation: \"Inverse of square on nonnegative domain is square root.\" },\n        { text: \"The function \\\\(f(x)=\\\\cos x\\\\) is:\", options: [\"One-to-one on \\\\([0,\\\\pi]\\\\)\", \"One-to-one on \\\\(\\\\mathbb{R}\\\\)\", \"Not one-to-one on any interval\", \"One-to-one on \\\\([-\\\\pi\/2,\\\\pi\/2]\\\\)\"], correct: 0, explanation: \"Cosine is decreasing on [0,\u03c0], hence one-to-one.\" },\n        { text: \"The horizontal stretch of \\\\(f(x)\\\\) by a factor of 2 is represented by:\", options: [\"\\\\(f(2x)\\\\)\", \"\\\\(f(x\/2)\\\\)\", \"\\\\(2f(x)\\\\)\", \"\\\\(\\\\frac12 f(x)\\\\)\"], correct: 1, explanation: \"f(x\/2) stretches horizontally by factor 2.\" },\n        { text: \"\\\\(\\\\log_3 27 - \\\\log_3 3\\\\) equals:\", options: [\"2\", \"3\", \"1\", \"0\"], correct: 0, explanation: \"log\u208327=3, log\u20833=1, difference=2.\" },\n        { text: \"The range of \\\\(f(x)=\\\\frac{3}{x-2}\\\\) is:\", options: [\"\\\\(\\\\mathbb{R}\\\\setminus\\\\{0\\\\}\\\\)\", \"\\\\(\\\\mathbb{R}\\\\setminus\\\\{2\\\\}\\\\)\", \"\\\\(\\\\mathbb{R}\\\\)\", \"\\\\((0,\\\\infty)\\\\)\"], correct: 0, explanation: \"Rational function can take any value except 0.\" },\n        { text: \"If \\\\(f\\\\) is a function such that \\\\(f(-x)=-f(x)\\\\) for all \\\\(x\\\\), then \\\\(f\\\\) is:\", options: [\"Even\", \"Odd\", \"Periodic\", \"Constant\"], correct: 1, explanation: \"Definition of odd function.\" },\n        { text: \"The graph of \\\\(y=f(x)\\\\) is shifted left by 4 units. The new equation is:\", options: [\"\\\\(y=f(x+4)\\\\)\", \"\\\\(y=f(x-4)\\\\)\", \"\\\\(y=f(x)+4\\\\)\", \"\\\\(y=f(x)-4\\\\)\"], correct: 0, explanation: \"Left shift: replace x with x+4.\" },\n        { text: \"\\\\(\\\\arccos(\\\\frac12)\\\\) equals:\", options: [\"\\\\(\\\\frac{\\\\pi}{6}\\\\)\", \"\\\\(\\\\frac{\\\\pi}{3}\\\\)\", \"\\\\(\\\\frac{\\\\pi}{4}\\\\)\", \"\\\\(\\\\frac{\\\\pi}{2}\\\\)\"], correct: 1, explanation: \"cos(\u03c0\/3)=1\/2.\" },\n        { text: \"If \\\\(f(x)=\\\\frac{x^2-1}{x-1}\\\\) for \\\\(x\\\\neq1\\\\), the limit as \\\\(x\\\\to1\\\\) is:\", options: [\"2\", \"0\", \"1\", \"Does not exist\"], correct: 0, explanation: \"Simplify to x+1, limit=2.\" },\n        { text: \"The function \\\\(f(x)=x^2\\\\) restricted to which domain becomes one-to-one?\", options: [\"\\\\(\\\\mathbb{R}\\\\)\", \"\\\\([0,\\\\infty)\\\\)\", \"\\\\((-1,1)\\\\)\", \"\\\\([-1,1]\\\\)\"], correct: 1, explanation: \"Square function is one-to-one on nonnegative reals.\" },\n        { text: \"The y-intercept of \\\\(f(x)=e^{x+1}\\\\) is:\", options: [\"\\\\(e\\\\)\", \"1\", \"0\", \"\\\\(e^2\\\\)\"], correct: 0, explanation: \"f(0)=e\u00b9=e.\" },\n        { text: \"Which of the following is equivalent to \\\\(\\\\ln(ab)\\\\)?\", options: [\"\\\\(\\\\ln a + \\\\ln b\\\\)\", \"\\\\(\\\\ln a \\\\cdot \\\\ln b\\\\)\", \"\\\\(\\\\ln(a+b)\\\\)\", \"\\\\(\\\\ln a - \\\\ln b\\\\)\"], correct: 0, explanation: \"Log product rule.\" },\n        { text: \"The graph of \\\\(y=\\\\cot x\\\\) has vertical asymptotes at:\", options: [\"\\\\(x=n\\\\pi\\\\)\", \"\\\\(x=\\\\frac{\\\\pi}{2}+n\\\\pi\\\\)\", \"\\\\(x=2n\\\\pi\\\\)\", \"\\\\(x=n\\\\pi\/2\\\\)\"], correct: 0, explanation: \"cot x = cos x\/sin x, undefined when sin x=0 at x=n\u03c0.\" },\n        { text: \"If \\\\(f(x)=\\\\log_{10} x\\\\), then \\\\(f(100)-f(10)\\\\) equals:\", options: [\"1\", \"2\", \"3\", \"0\"], correct: 0, explanation: \"log\u2081\u2080100=2, log\u2081\u208010=1, difference=1.\" },\n        { text: \"The graph of \\\\(y=f(2x)\\\\) compared to \\\\(y=f(x)\\\\) is:\", options: [\"Stretched horizontally\", \"Compressed horizontally\", \"Stretched vertically\", \"Compressed vertically\"], correct: 1, explanation: \"f(2x) compresses horizontally by factor 1\/2.\" },\n        { text: \"The domain of \\\\(f(x)=\\\\sqrt{4-x^2}\\\\) is:\", options: [\"\\\\([-2,2]\\\\)\", \"\\\\((-\\\\infty,-2]\\\\cup[2,\\\\infty)\\\\)\", \"\\\\((-2,2)\\\\)\", \"\\\\([0,4]\\\\)\"], correct: 0, explanation: \"4-x\u00b2 \u2265 0 \u21d2 |x| \u2264 2.\" },\n        { text: \"\\\\(\\\\sin(\\\\arcsin(0.5))\\\\) equals:\", options: [\"0.5\", \"\\\\(\\\\pi\/6\\\\)\", \"1\", \"0\"], correct: 0, explanation: \"Arcsin undoes sine.\" },\n        { text: \"If \\\\(f(x)=2^x\\\\), then \\\\(f^{-1}(8)\\\\) equals:\", options: [\"3\", \"4\", \"2\", \"1\"], correct: 0, explanation: \"2\u00b3=8, so inverse gives 3.\" },\n        { text: \"The function \\\\(f(x)=\\\\frac{1}{x}\\\\) has which type of symmetry?\", options: [\"Even\", \"Odd\", \"Periodic\", \"No symmetry\"], correct: 1, explanation: \"f(-x) = -1\/x = -f(x), so odd.\" },\n        { text: \"The range of \\\\(f(x)=\\\\arctan x\\\\) is:\", options: [\"\\\\([-\\\\pi\/2,\\\\pi\/2]\\\\)\", \"\\\\((-\\\\pi\/2,\\\\pi\/2)\\\\)\", \"\\\\([0,\\\\pi]\\\\)\", \"\\\\((0,\\\\pi)\\\\)\"], correct: 1, explanation: \"Arctan range is open interval (-\u03c0\/2,\u03c0\/2).\" },\n        { text: \"If \\\\(f(x)=3x-2\\\\) and \\\\(g(x)=x+4\\\\), then \\\\((f+g)(2)\\\\) equals:\", options: [\"10\", \"8\", \"12\", \"6\"], correct: 0, explanation: \"f(2)=4, g(2)=6, sum=10.\" },\n        { text: \"The vertical shift of \\\\(f(x)\\\\) downward by 5 units is represented by:\", options: [\"\\\\(f(x)-5\\\\)\", \"\\\\(f(x)+5\\\\)\", \"\\\\(f(x-5)\\\\)\", \"\\\\(f(x+5)\\\\)\"], correct: 0, explanation: \"Subtracting constant shifts down.\" },\n        { text: \"The asymptote of \\\\(y=\\\\log_2(x-3)\\\\) is:\", options: [\"\\\\(x=3\\\\)\", \"\\\\(y=3\\\\)\", \"\\\\(x=0\\\\)\", \"\\\\(y=0\\\\)\"], correct: 0, explanation: \"Vertical asymptote where argument = 0.\" },\n        { text: \"If \\\\(f(x)=\\\\sin x\\\\) and \\\\(g(x)=x^2\\\\), then \\\\(f(g(\\\\pi\/2))\\\\) equals:\", options: [\"\\\\(\\\\sin(\\\\pi^2\/4)\\\\)\", \"1\", \"0\", \"\\\\(\\\\pi^2\/4\\\\)\"], correct: 0, explanation: \"g(\u03c0\/2)=\u03c0\u00b2\/4, then f of that.\" },\n        { text: \"Which function has a graph that is symmetric about the y-axis?\", options: [\"\\\\(f(x)=x^3\\\\)\", \"\\\\(f(x)=|x|\\\\)\", \"\\\\(f(x)=\\\\sin x\\\\)\", \"\\\\(f(x)=\\\\tan x\\\\)\"], correct: 1, explanation: \"|x| is even, symmetric about y-axis.\" },\n        { text: \"\\\\(\\\\log_2 32\\\\) equals:\", options: [\"5\", \"4\", \"6\", \"3\"], correct: 0, explanation: \"2\u2075=32.\" },\n        { text: \"The period of \\\\(y=\\\\tan(2x)\\\\) is:\", options: [\"\\\\(\\\\frac{\\\\pi}{2}\\\\)\", \"\\\\(\\\\pi\\\\)\", \"\\\\(2\\\\pi\\\\)\", \"\\\\(\\\\frac{\\\\pi}{4}\\\\)\"], correct: 0, explanation: \"Period of tan(kx) = \u03c0\/k = \u03c0\/2.\" },\n        { text: \"If \\\\(f(x)=\\\\sqrt{x-1}\\\\), then the domain is:\", options: [\"\\\\([1,\\\\infty)\\\\)\", \"\\\\((1,\\\\infty)\\\\)\", \"\\\\((-\\\\infty,1]\\\\)\", \"\\\\(\\\\mathbb{R}\\\\)\"], correct: 0, explanation: \"Square root requires x-1 \u2265 0 \u21d2 x \u2265 1.\" },\n        { text: \"\\\\(\\\\ln e^3 - \\\\ln 1\\\\) equals:\", options: [\"3\", \"0\", \"1\", \"\\\\(e^3\\\\)\"], correct: 0, explanation: \"ln e\u00b3=3, ln1=0, difference=3.\" },\n        { text: \"The function \\\\(f:\\\\mathbb{R}\\\\to\\\\mathbb{R}, f(x)=x^3\\\\) is:\", options: [\"One-to-one but not onto\", \"Onto but not one-to-one\", \"Bijective\", \"Neither one-to-one nor onto\"], correct: 2, explanation: \"Cubic function is both injective and surjective on reals.\" },\n        { text: \"If the graph of \\\\(y=\\\\sqrt{x}\\\\) is shifted up by 3 units, the equation becomes:\", options: [\"\\\\(y=\\\\sqrt{x}+3\\\\)\", \"\\\\(y=\\\\sqrt{x+3}\\\\)\", \"\\\\(y=\\\\sqrt{x}-3\\\\)\", \"\\\\(y=\\\\sqrt{x-3}\\\\)\"], correct: 0, explanation: \"Vertical shift adds to output.\" },\n        { text: \"\\\\(\\\\arcsin(1)\\\\) equals:\", options: [\"\\\\(\\\\frac{\\\\pi}{2}\\\\)\", \"\\\\(\\\\pi\\\\)\", \"0\", \"\\\\(-\\\\frac{\\\\pi}{2}\\\\)\"], correct: 0, explanation: \"sin(\u03c0\/2)=1, and \u03c0\/2 is in range of arcsin.\" },\n        { text: \"The graph of \\\\(y=2\\\\sin x\\\\) compared to \\\\(y=\\\\sin x\\\\) has:\", options: [\"Double the amplitude\", \"Double the period\", \"Half the amplitude\", \"Half the period\"], correct: 0, explanation: \"Amplitude changes from 1 to 2.\" },\n        { text: \"The vertical asymptote of \\\\(y=\\\\ln(x-2)\\\\) is at:\", options: [\"\\\\(x=2\\\\)\", \"\\\\(x=0\\\\)\", \"\\\\(y=2\\\\)\", \"\\\\(y=0\\\\)\"], correct: 0, explanation: \"ln argument zero gives vertical asymptote.\" },\n        { text: \"If \\\\(f(x)=x^2+1\\\\) and \\\\(g(x)=2x\\\\), then \\\\((f\\\\circ g)(3)\\\\) equals:\", options: [\"37\", \"19\", \"10\", \"13\"], correct: 0, explanation: \"g(3)=6, f(6)=36+1=37.\" },\n        { text: \"The range of \\\\(f(x)=\\\\frac{1}{x}\\\\) for \\\\(x>0\\\\) is:\", options: [\"\\\\((0,\\\\infty)\\\\)\", \"\\\\(\\\\mathbb{R}\\\\)\", \"\\\\((-\\\\infty,0)\\\\)\", \"\\\\(\\\\mathbb{R}\\\\setminus\\\\{0\\\\}\\\\)\"], correct: 0, explanation: \"For positive x, 1\/x is positive.\" },\n        { text: \"A function that satisfies \\\\(f(a)=f(b)\\\\) implies \\\\(a=b\\\\) is:\", options: [\"Surjective\", \"Injective\", \"Bijective\", \"Constant\"], correct: 1, explanation: \"Definition of injective (one-to-one).\" },\n        { text: \"\\\\(\\\\log_4 64\\\\) equals:\", options: [\"3\", \"4\", \"2\", \"1\"], correct: 0, explanation: \"4\u00b3=64.\" },\n        { text: \"The graph of \\\\(y=\\\\sec x\\\\) is undefined when:\", options: [\"\\\\(\\\\cos x=0\\\\)\", \"\\\\(\\\\sin x=0\\\\)\", \"\\\\(\\\\cos x=1\\\\)\", \"\\\\(\\\\sin x=1\\\\)\"], correct: 0, explanation: \"sec x = 1\/cos x.\" },\n        { text: \"If \\\\(f(x)=\\\\sqrt{x+4}\\\\), then \\\\(f^{-1}(x)\\\\) for \\\\(x\\\\ge 0\\\\) is:\", options: [\"\\\\(x^2-4\\\\)\", \"\\\\(x^2+4\\\\)\", \"\\\\(\\\\sqrt{x}-4\\\\)\", \"\\\\((x-4)^2\\\\)\"], correct: 0, explanation: \"Solve y=\u221a(x+4) \u21d2 y\u00b2=x+4 \u21d2 x=y\u00b2-4.\" },\n        { text: \"The amplitude of \\\\(y=-3\\\\sin x\\\\) is:\", options: [\"3\", \"-3\", \"1\", \"0\"], correct: 0, explanation: \"Amplitude is absolute value of coefficient, 3.\" },\n        { text: \"Which of the following is NOT a function?\", options: [\"\\\\(y=x^2\\\\)\", \"\\\\(y^2=x\\\\)\", \"\\\\(y=|x|\\\\)\", \"\\\\(y=\\\\sqrt{x}\\\\)\"], correct: 1, explanation: \"y\u00b2=x gives two y for positive x, not a function.\" },\n        { text: \"The horizontal asymptote of \\\\(y=\\\\frac{2x^2}{x^2+1}\\\\) is:\", options: [\"\\\\(y=2\\\\)\", \"\\\\(y=0\\\\)\", \"\\\\(y=1\\\\)\", \"No horizontal asymptote\"], correct: 0, explanation: \"Leading coefficients ratio = 2.\" },\n        { text: \"\\\\(\\\\ln\\\\left(\\\\frac{e^4}{e^2}\\\\right)\\\\) equals:\", options: [\"2\", \"4\", \"6\", \"1\"], correct: 0, explanation: \"e\u2074\/e\u00b2=e\u00b2, ln(e\u00b2)=2.\" },\n        { text: \"If \\\\(f(x)=5\\\\), then \\\\(f\\\\) is a:\", options: [\"Constant function\", \"Linear function\", \"Quadratic function\", \"Rational function\"], correct: 0, explanation: \"Constant output for all inputs.\" },\n        { text: \"The graph of \\\\(y=f(-x)\\\\) is obtained by reflecting \\\\(f(x)\\\\) across:\", options: [\"The y-axis\", \"The x-axis\", \"The line \\\\(y=x\\\\)\", \"The origin\"], correct: 0, explanation: \"f(-x) reflects across y-axis.\" },\n        { text: \"The maximum value of \\\\(y=2+\\\\sin x\\\\) is:\", options: [\"3\", \"2\", \"1\", \"4\"], correct: 0, explanation: \"sin x max =1, so 2+1=3.\" },\n        { text: \"\\\\(\\\\log_3 81\\\\) equals:\", options: [\"4\", \"5\", \"3\", \"2\"], correct: 0, explanation: \"3\u2074=81.\" },\n        { text: \"The function \\\\(f(x)=\\\\frac{x}{|x|}\\\\) for \\\\(x\\\\neq0\\\\) has a range of:\", options: [\"\\\\(\\\\{-1,1\\\\}\\\\)\", \"\\\\([-1,1]\\\\)\", \"\\\\((-\\\\infty,\\\\infty)\\\\)\", \"\\\\(\\\\{0,1\\\\}\\\\)\"], correct: 0, explanation: \"Output is 1 for x>0, -1 for x<0.\" },\n        { text: \"If \\\\(f(x)=x^2+3x-4\\\\), then \\\\(f(2)\\\\) equals:\", options: [\"6\", \"4\", \"8\", \"10\"], correct: 0, explanation: \"4+6-4=6.\" },\n        { text: \"The period of \\\\(y=\\\\cos(\\\\frac{x}{2})\\\\) is:\", options: [\"\\\\(4\\\\pi\\\\)\", \"\\\\(2\\\\pi\\\\)\", \"\\\\(\\\\pi\\\\)\", \"\\\\(\\\\frac{\\\\pi}{2}\\\\)\"], correct: 0, explanation: \"Period = 2\u03c0\/(1\/2)=4\u03c0.\" },\n        { text: \"The graph of \\\\(y=\\\\log_2 x\\\\) passes through which point?\", options: [\"\\\\((1,0)\\\\)\", \"\\\\((0,1)\\\\)\", \"\\\\((2,1)\\\\)\", \"Both A and C\"], correct: 3, explanation: \"log\u20821=0, log\u20822=1.\" },\n        { text: \"If \\\\(f\\\\) is a one-to-one function and \\\\(f(1)=4\\\\), then \\\\(f^{-1}(4)\\\\) is:\", options: [\"1\", \"4\", \"\\\\(\\\\frac14\\\\)\", \"0\"], correct: 0, explanation: \"Inverse swaps.\" },\n        { text: \"The y-intercept of \\\\(f(x)=\\\\log_2(x+1)\\\\) is:\", options: [\"0\", \"1\", \"2\", \"Undefined\"], correct: 0, explanation: \"f(0)=log\u20821=0.\" },\n        { text: \"The vertical scaling of \\\\(f(x)\\\\) by a factor of \\\\(\\\\frac12\\\\) produces:\", options: [\"\\\\(\\\\frac12 f(x)\\\\)\", \"\\\\(f(\\\\frac{x}{2})\\\\)\", \"\\\\(f(2x)\\\\)\", \"\\\\(2f(x)\\\\)\"], correct: 0, explanation: \"Vertical scaling multiplies output.\" },\n        { text: \"\\\\(\\\\cos(\\\\arccos(0.8))\\\\) equals:\", options: [\"0.8\", \"\\\\(\\\\arccos(0.8)\\\\)\", \"\\\\(0.8\\\\cos\\\\)\", \"\\\\(\\\\pi\/2\\\\)\"], correct: 0, explanation: \"arccos undoes cosine.\" },\n        { text: \"If \\\\(f(x)=x+\\\\frac{1}{x}\\\\), then \\\\(f(\\\\frac{1}{x})\\\\) equals:\", options: [\"\\\\(f(x)\\\\)\", \"\\\\(-f(x)\\\\)\", \"\\\\(\\\\frac{1}{f(x)}\\\\)\", \"\\\\(x+\\\\frac{1}{x}\\\\)\"], correct: 0, explanation: \"f(1\/x)=1\/x + x = f(x).\" },\n        { text: \"The horizontal line test is used to determine if a function is:\", options: [\"One-to-one\", \"Onto\", \"Continuous\", \"Differentiable\"], correct: 0, explanation: \"Horizontal line tests injectivity.\" },\n        { text: \"The range of \\\\(f(x)=\\\\sqrt{16-x^2}\\\\) is:\", options: [\"\\\\([0,4]\\\\)\", \"\\\\([-4,4]\\\\)\", \"\\\\([0,\\\\infty)\\\\)\", \"\\\\((-\\\\infty,4]\\\\)\"], correct: 0, explanation: \"Square root gives nonnegative outputs, maximum 4.\" },\n        { text: \"If \\\\(f(x)=e^x\\\\), then \\\\(f(\\\\ln 5)\\\\) equals:\", options: [\"5\", \"\\\\(e^5\\\\)\", \"\\\\(\\\\ln 5\\\\)\", \"1\"], correct: 0, explanation: \"e^(ln5)=5.\" },\n        { text: \"The graph of \\\\(y=f(x)+c\\\\) with \\\\(c<0\\\\) is shifted:\", options: [\"Downward\", \"Upward\", \"Right\", \"Left\"], correct: 0, explanation: \"Negative c shifts down.\" },\n        { text: \"\\\\(\\\\log_2 16 \\\\cdot \\\\log_3 27\\\\) equals:\", options: [\"12\", \"9\", \"8\", \"6\"], correct: 0, explanation: \"4\u00d73=12.\" },\n        { text: \"If \\\\(f(x)=2^x\\\\) and \\\\(g(x)=\\\\log_2 x\\\\), then \\\\((f\\\\circ g)(8)\\\\) equals:\", options: [\"8\", \"3\", \"2\", \"1\"], correct: 0, explanation: \"g(8)=3, f(3)=2\u00b3=8.\" },\n        { text: \"The function \\\\(f(x)=\\\\tan x\\\\) is periodic with period:\", options: [\"\\\\(\\\\pi\\\\)\", \"\\\\(2\\\\pi\\\\)\", \"\\\\(\\\\frac{\\\\pi}{2}\\\\)\", \"\\\\(4\\\\pi\\\\)\"], correct: 0, explanation: \"Period of tan is \u03c0.\" },\n        { text: \"The x-intercept of \\\\(f(x)=\\\\ln(x-1)\\\\) is:\", options: [\"2\", \"1\", \"0\", \"e\"], correct: 0, explanation: \"ln(x-1)=0 \u21d2 x-1=1 \u21d2 x=2.\" },\n        { text: \"If \\\\(f(x)=x^2\\\\), then the graph of \\\\(f(x-3)\\\\) is:\", options: [\"The same parabola shifted 3 units right\", \"The same parabola shifted 3 units left\", \"The same parabola shifted 3 units up\", \"The same parabola shifted 3 units down\"], correct: 0, explanation: \"Horizontal shift right.\" },\n        { text: \"\\\\(\\\\arcsin(\\\\frac{\\\\sqrt{3}}{2})\\\\) equals:\", options: [\"\\\\(\\\\frac{\\\\pi}{3}\\\\)\", \"\\\\(\\\\frac{\\\\pi}{6}\\\\)\", \"\\\\(\\\\frac{\\\\pi}{4}\\\\)\", \"\\\\(\\\\frac{\\\\pi}{2}\\\\)\"], correct: 0, explanation: \"sin(\u03c0\/3)=\u221a3\/2.\" },\n        { text: \"The graph of \\\\(y=|f(x)|\\\\) is obtained by:\", options: [\"Reflecting negative parts of \\\\(f(x)\\\\) above the x-axis\", \"Reflecting the entire graph across the x-axis\", \"Reflecting the entire graph across the y-axis\", \"Shifting the graph vertically\"], correct: 0, explanation: \"Absolute value makes negative y positive.\" },\n        { text: \"If \\\\(f(x)=2x^2-3x+1\\\\), then \\\\(f(0)\\\\) equals:\", options: [\"1\", \"0\", \"-1\", \"2\"], correct: 0, explanation: \"Constant term is 1.\" },\n        { text: \"The asymptote(s) of \\\\(y=\\\\frac{1}{x-2}+3\\\\) is\/are:\", options: [\"\\\\(x=2, y=3\\\\)\", \"\\\\(x=-2, y=3\\\\)\", \"\\\\(x=2, y=-3\\\\)\", \"\\\\(x=-2, y=-3\\\\)\"], correct: 0, explanation: \"Vertical asymptote at x=2, horizontal at y=3.\" },\n        { text: \"\\\\(\\\\ln(e^2 \\\\cdot e^3)\\\\) equals:\", options: [\"5\", \"6\", \"1\", \"0\"], correct: 0, explanation: \"e\u00b2\u00b7e\u00b3=e\u2075, ln(e\u2075)=5.\" },\n        { text: \"Which function is its own inverse?\", options: [\"\\\\(f(x)=x\\\\)\", \"\\\\(f(x)=2x\\\\)\", \"\\\\(f(x)=x^2\\\\)\", \"\\\\(f(x)=\\\\sqrt{x}\\\\)\"], correct: 0, explanation: \"Identity function is its own inverse.\" }\n    ];\n\n    \/\/ App state\n    let currentIndex = 0;\n    let userAnswers = new Array(MCQ_SET.length).fill(null);\n    let answerStatus = new Array(MCQ_SET.length).fill(false);\n    let score = 0;\n    let startTime = Date.now();\n    let timerInterval = null;\n    let isTimerRunning = true;\n    let pausedElapsed = 0;\n\n    \/\/ DOM elements\n    const questionTextEl = document.getElementById('questionText');\n    const optionsContainer = document.getElementById('optionsContainer');\n    const prevBtn = document.getElementById('prevBtn');\n    const nextBtn = document.getElementById('nextBtn');\n    const resetBtn = document.getElementById('resetBtn');\n    const questionCounterSpan = document.getElementById('questionCounter');\n    const progressFill = document.getElementById('progressFill');\n    const feedbackMsgDiv = document.getElementById('feedbackMsg');\n    const feedbackTextSpan = document.getElementById('feedbackText');\n    const questionGrid = document.getElementById('questionGrid');\n    const attemptedStatsSpan = document.getElementById('attemptedStats');\n    const scoreDisplaySpan = document.getElementById('scoreDisplay');\n    const clockDisplaySpan = document.getElementById('clockDisplay');\n    const clockStartBtn = document.getElementById('clockStartBtn');\n    const clockStopBtn = document.getElementById('clockStopBtn');\n\n    function startTimer() {\n        if (timerInterval) clearInterval(timerInterval);\n        startTime = Date.now() - (pausedElapsed || 0);\n        timerInterval = setInterval(updateClock, 1000);\n        isTimerRunning = true;\n    }\n    function stopTimer() {\n        if (timerInterval) { clearInterval(timerInterval); timerInterval = null; }\n        pausedElapsed = Date.now() - startTime;\n        isTimerRunning = false;\n    }\n    function updateClock() {\n        const elapsed = Math.floor((Date.now() - startTime) \/ 1000);\n        const hours = Math.floor(elapsed \/ 3600);\n        const minutes = Math.floor((elapsed % 3600) \/ 60);\n        const seconds = elapsed % 60;\n        clockDisplaySpan.textContent = `${hours.toString().padStart(2,'0')}:${minutes.toString().padStart(2,'0')}:${seconds.toString().padStart(2,'0')}`;\n    }\n    function resetTimer() { stopTimer(); pausedElapsed=0; startTime=Date.now(); updateClock(); startTimer(); }\n\n    function buildNavigatorGrid() {\n        questionGrid.innerHTML = '';\n        for (let i=0; i<MCQ_SET.length; i++) {\n            const link = document.createElement('div');\n            link.className = 'q-link';\n            if (userAnswers[i] !== null) link.classList.add('attempted');\n            else link.classList.add('unattempted');\n            if (i === currentIndex) link.classList.add('current');\n            link.textContent = i+1;\n            link.addEventListener('click', (idx => () => { currentIndex = idx; renderQuestion(); })(i));\n            questionGrid.appendChild(link);\n        }\n    }\n    function updateNavigatorColors() {\n        const allLinks = questionGrid.children;\n        for (let i=0; i<allLinks.length; i++) {\n            allLinks[i].classList.remove('attempted', 'unattempted', 'current', 'incorrect-final');\n            if (userAnswers[i] !== null) {\n                if (answerStatus[i] === false) allLinks[i].classList.add('incorrect-final');\n                else allLinks[i].classList.add('attempted');\n            } else allLinks[i].classList.add('unattempted');\n            if (i === currentIndex) allLinks[i].classList.add('current');\n        }\n        const attemptedCount = userAnswers.filter(a => a !== null).length;\n        attemptedStatsSpan.textContent = `${attemptedCount} \/ ${MCQ_SET.length} Attempted`;\n        scoreDisplaySpan.innerHTML = `\u2b50 Score: ${score} \/ ${MCQ_SET.length}`;\n    }\n    function renderMath() { if (window.MathJax) MathJax.typesetPromise().catch(e=>console.log(e)); }\n    \n    function showFeedback(isCorrect, msg) {\n        feedbackMsgDiv.classList.add('show');\n        if (isCorrect) {\n            feedbackMsgDiv.classList.remove('wrong-feedback');\n            feedbackMsgDiv.classList.add('correct-feedback');\n            feedbackTextSpan.innerHTML = `\u2705 <strong>Correct!<\/strong><br><br>\ud83d\udcd6 ${msg}`;\n        } else {\n            feedbackMsgDiv.classList.remove('correct-feedback');\n            feedbackMsgDiv.classList.add('wrong-feedback');\n            feedbackTextSpan.innerHTML = msg;\n        }\n        renderMath();\n    }\n    function hideFeedback() { feedbackMsgDiv.classList.remove('show'); }\n    \n    function renderQuestion() {\n        const q = MCQ_SET[currentIndex];\n        questionTextEl.innerHTML = q.text;\n        optionsContainer.innerHTML = '';\n        q.options.forEach((opt, idx) => {\n            const letter = String.fromCharCode(65+idx);\n            const div = document.createElement('div');\n            div.className = 'option-card';\n            if (userAnswers[currentIndex] === idx) div.classList.add('selected');\n            div.innerHTML = `<div class=\"opt-letter\">${letter}.<\/div><div class=\"opt-text\">${opt}<\/div>`;\n            div.addEventListener('click', () => selectOption(idx));\n            optionsContainer.appendChild(div);\n        });\n        questionCounterSpan.innerText = `Q${currentIndex+1} of ${MCQ_SET.length}`;\n        progressFill.style.width = `${((currentIndex+1)\/MCQ_SET.length)*100}%`;\n        updateNavigatorColors();\n        const ans = userAnswers[currentIndex];\n        if (ans !== null) {\n            const isCor = (ans === q.correct);\n            showFeedback(isCor, isCor ? q.explanation : `\u274c <strong>Incorrect<\/strong><br><br>\u2714\ufe0f <strong>Correct answer:<\/strong> ${String.fromCharCode(65+q.correct)}. ${q.options[q.correct]}<br><br>\ud83d\udcd6 ${q.explanation}`);\n        } else hideFeedback();\n        renderMath();\n    }\n    \n    function selectOption(selectedIdx) {\n        const q = MCQ_SET[currentIndex];\n        if (userAnswers[currentIndex] === selectedIdx) return;\n        userAnswers[currentIndex] = selectedIdx;\n        const isCor = (selectedIdx === q.correct);\n        const old = answerStatus[currentIndex];\n        if (old !== isCor) {\n            answerStatus[currentIndex] = isCor;\n            if (isCor) score++;\n            else score--;\n        }\n        renderQuestion();\n        updateNavigatorColors();\n        if (userAnswers.every(a => a !== null)) {\n            const percentage = Math.round((score\/ MCQ_SET.length)*100);\n            feedbackMsgDiv.classList.add('show');\n            feedbackMsgDiv.style.background = \"#e8f4f8\";\n            feedbackMsgDiv.style.borderLeft = \"8px solid #1f6e8c\";\n            feedbackTextSpan.innerHTML = `\ud83c\udf89 <strong>Quiz Completed!<\/strong><br><br>\ud83d\udcca Final score: <strong>${score}\/${MCQ_SET.length} (${percentage}%)<\/strong><br><br>\u26a0\ufe0f Red boxes show incorrect answers.<br><br>\u23f1\ufe0f Time: <strong>${clockDisplaySpan.textContent}<\/strong>`;\n            renderMath();\n        }\n    }\n    \n    function goPrev() { if (currentIndex > 0) { currentIndex--; renderQuestion(); } }\n    function goNext() { if (currentIndex < MCQ_SET.length-1) { currentIndex++; renderQuestion(); } }\n    function resetQuiz() {\n        userAnswers.fill(null);\n        answerStatus.fill(false);\n        score = 0;\n        currentIndex = 0;\n        resetTimer();\n        renderQuestion();\n        hideFeedback();\n        updateNavigatorColors();\n        feedbackMsgDiv.style.background = \"\";\n        feedbackMsgDiv.style.borderLeft = \"\";\n    }\n    \n    prevBtn.addEventListener('click', goPrev);\n    nextBtn.addEventListener('click', goNext);\n    resetBtn.addEventListener('click', resetQuiz);\n    clockStartBtn.addEventListener('click', startTimer);\n    clockStopBtn.addEventListener('click', stopTimer);\n    window.addEventListener('keydown', (e) => {\n        if (e.key === 'ArrowLeft') { goPrev(); e.preventDefault(); }\n        else if (e.key === 'ArrowRight') { goNext(); e.preventDefault(); }\n    });\n    \n    buildNavigatorGrid();\n    startTimer();\n    renderQuestion();\n<\/script>\n\n\n\n\n<div class=\"wp-block-bplugins-custom-html\" id='bPluginsCustomHtml-1'>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>MAFtrix \u2014 Free Online Calculators for Math, Finance &#038; Everyday Life<\/title>\n<meta name=\"description\" content=\"MAFtrix offers 30+ free online calculators \u2014 mortgage, loan, BMI, percentage, GPA and more. Fast, accurate, no sign-up. Calculate online in seconds.\">\n<meta name=\"keywords\" content=\"online calculator, free online calculator, calculate online, math calculators, finance calculators\">\n<meta name=\"robots\" content=\"index, follow\">\n<meta property=\"og:title\" content=\"MAFtrix \u2014 Free Online Calculators\">\n<meta property=\"og:description\" content=\"30+ free calculators for finance, math, health and everyday life. Instant, accurate, no sign-up required.\">\n<meta property=\"og:type\" content=\"website\">\n<link rel=\"icon\" href=\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='22' fill='%230F1C2E'\/%3E%3Crect x='28' y='24' width='44' height='52' rx='6' fill='%23F7F8FA'\/%3E%3Crect x='34' y='31' width='32' height='12' rx='2' fill='%230EA5A0'\/%3E%3Ccircle cx='40' cy='54' r='3.4' fill='%230F1C2E'\/%3E%3Ccircle cx='50' cy='54' r='3.4' fill='%230F1C2E'\/%3E%3Ccircle cx='60' cy='54' r='3.4' fill='%23F5A623'\/%3E%3Ccircle cx='40' cy='64' r='3.4' fill='%230F1C2E'\/%3E%3Ccircle cx='50' cy='64' r='3.4' fill='%230F1C2E'\/%3E%3Ccircle cx='60' cy='64' r='3.4' fill='%230F1C2E'\/%3E%3C\/svg%3E\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Space+Grotesk:wght@400;500;600;700&#038;family=Inter:wght@400;500;600;700&#038;family=IBM+Plex+Mono:wght@400;500;600&#038;display=swap\" rel=\"stylesheet\">\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/Chart.js\/4.4.3\/chart.umd.min.js\"><\/script>\n<script type=\"application\/ld+json\">\n{\"@context\":\"https:\/\/schema.org\",\"@type\":\"WebSite\",\"name\":\"MAFtrix\",\"description\":\"Free online calculators for math, finance, health and everyday life.\",\"url\":\"https:\/\/www.maftrix.com\"}\n<\/script>\n<style>\n:root{\n  --ink:#0F1C2E;\n  --ink-2:#16273D;\n  --ink-3:#1E3350;\n  --teal:#0EA5A0;\n  --teal-dark:#0B8783;\n  --amber:#F5A623;\n  --bg:#F7F8FA;\n  --surface:#FFFFFF;\n  --line:#E3E7ED;\n  --text:#1F2937;\n  --text-soft:#5B6472;\n  --success:#1E9E6B;\n  --danger:#D6503B;\n  --radius:14px;\n  --shadow:0 4px 16px rgba(15,28,46,.06), 0 1px 3px rgba(15,28,46,.08);\n  --shadow-lg:0 20px 48px rgba(15,28,46,.16);\n}\n*{box-sizing:border-box;}\nhtml{scroll-behavior:smooth;font-size:17.5px;}\nbody{margin:0;font-family:'Inter',sans-serif;color:var(--text);background:var(--bg);line-height:1.6;}\nh1,h2,h3,h4,h5{font-family:'Space Grotesk',sans-serif;color:var(--ink);margin:0 0 .5em;line-height:1.15;letter-spacing:-.01em;}\n.mono{font-family:'IBM Plex Mono',monospace;}\na{color:inherit;text-decoration:none;}\nimg{max-width:100%;display:block;}\nbutton{font-family:inherit;cursor:pointer;}\n.wrap{max-width:1180px;margin:0 auto;padding:0 24px;}\nsection.page{display:none;}\nsection.page.active{display:block;animation:fadein .35s ease;}\n@keyframes fadein{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}\n:focus-visible{outline:2px solid var(--teal);outline-offset:2px;}\n\n\/* ---------- Ticked ruler divider (signature motif) ---------- *\/\n.ticks{height:14px;background-image:repeating-linear-gradient(90deg, var(--line) 0 1px, transparent 1px 12px);opacity:.9;}\n.ticks.dark{background-image:repeating-linear-gradient(90deg, rgba(255,255,255,.18) 0 1px, transparent 1px 12px);}\n\n\/* ---------- Header ---------- *\/\nheader{position:sticky;top:0;z-index:100;background:rgba(15,28,46,.94);backdrop-filter:blur(10px);border-bottom:1px solid rgba(255,255,255,.08);}\n.nav{display:flex;align-items:center;justify-content:space-between;padding:14px 0;}\n.logo{display:flex;align-items:center;gap:10px;font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:1.3rem;color:#fff;}\n.logo-mark{width:34px;height:34px;border-radius:9px;background:linear-gradient(155deg,var(--teal),var(--teal-dark));display:flex;align-items:center;justify-content:center;color:#fff;font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:.95rem;}\n.logo .accent{color:var(--amber);}\nnav.links{display:flex;gap:2px;list-style:none;margin:0;padding:0;}\nnav.links a{color:rgba(255,255,255,.78);padding:10px 16px;border-radius:8px;font-size:.95rem;font-weight:500;transition:.2s;}\nnav.links a:hover{color:#fff;background:rgba(255,255,255,.08);}\nnav.links a.active{color:var(--ink);background:var(--amber);}\n.nav-cta{display:none;}\n.burger{display:none;flex-direction:column;gap:5px;background:none;border:0;padding:8px;}\n.burger span{width:22px;height:2px;background:#fff;border-radius:2px;}\n@media(max-width:860px){\n  nav.links{position:fixed;top:64px;left:0;right:0;background:var(--ink);flex-direction:column;padding:10px 24px 20px;gap:2px;transform:translateY(-130%);transition:transform .3s ease;border-bottom:1px solid rgba(255,255,255,.08);}\n  header.open nav.links{transform:translateY(0);}\n  .burger{display:flex;}\n}\n\n\/* ---------- Buttons ---------- *\/\n.btn{display:inline-flex;align-items:center;gap:8px;padding:13px 26px;border-radius:10px;font-weight:600;font-size:.95rem;border:1px solid transparent;transition:.2s;white-space:nowrap;}\n.btn-primary{background:var(--teal);color:#fff;}\n.btn-primary:hover{background:var(--teal-dark);transform:translateY(-1px);}\n.btn-amber{background:var(--amber);color:var(--ink);}\n.btn-amber:hover{filter:brightness(.95);transform:translateY(-1px);}\n.btn-outline{background:transparent;border-color:rgba(255,255,255,.35);color:#fff;}\n.btn-outline:hover{background:rgba(255,255,255,.1);}\n.btn-ghost{background:var(--surface);border-color:var(--line);color:var(--ink);}\n.btn-ghost:hover{border-color:var(--teal);color:var(--teal-dark);}\n.btn-block{width:100%;justify-content:center;}\n.btn-sm{padding:9px 16px;font-size:.85rem;}\n\n\/* ---------- Hero ---------- *\/\n.hero{background:radial-gradient(ellipse at 15% -10%, #1a3552 0%, var(--ink) 55%);color:#fff;padding:76px 0 0;}\n.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:48px;align-items:center;}\n.eyebrow{display:inline-flex;align-items:center;gap:8px;font-family:'IBM Plex Mono',monospace;font-size:.78rem;letter-spacing:.06em;color:var(--amber);background:rgba(245,166,35,.12);border:1px solid rgba(245,166,35,.35);padding:6px 12px;border-radius:20px;margin-bottom:22px;}\n.hero h1{color:#fff;font-size:3rem;margin-bottom:.4em;}\n.hero h1 em{font-style:normal;color:var(--teal);}\n.hero p.lead{color:rgba(255,255,255,.72);font-size:1.13rem;max-width:520px;margin-bottom:30px;}\n.search-bar{display:flex;background:#fff;border-radius:12px;padding:6px;box-shadow:var(--shadow-lg);max-width:520px;margin-bottom:26px;}\n.search-bar input{flex:1;border:0;padding:12px 14px;font-size:1rem;font-family:inherit;border-radius:8px;color:var(--ink);}\n.search-bar input:focus{outline:none;}\n.hero-actions{display:flex;gap:14px;flex-wrap:wrap;margin-bottom:40px;}\n.stat-row{display:flex;gap:34px;flex-wrap:wrap;}\n.stat b{display:block;font-family:'Space Grotesk',sans-serif;font-size:1.7rem;color:#fff;}\n.stat span{color:rgba(255,255,255,.55);font-size:.82rem;text-transform:uppercase;letter-spacing:.05em;}\n\n\/* hero device illustration *\/\n.device-wrap{display:flex;justify-content:center;position:relative;}\n.device-wrap svg{width:100%;max-width:380px;filter:drop-shadow(0 30px 60px rgba(0,0,0,.35));}\n\n\/* ---------- Section basics ---------- *\/\n.section{padding:76px 0;}\n.section.tight{padding:56px 0;}\n.section-head{max-width:660px;margin-bottom:44px;}\n.section-head .kicker{font-family:'IBM Plex Mono',monospace;font-size:.78rem;letter-spacing:.08em;color:var(--teal-dark);text-transform:uppercase;margin-bottom:10px;display:block;}\n.section-head p{color:var(--text-soft);font-size:1.05rem;}\n.section.on-dark{background:var(--ink);color:#fff;}\n.section.on-dark h2, .section.on-dark h3{color:#fff;}\n.section.on-dark .section-head p{color:rgba(255,255,255,.65);}\n.bg-alt{background:#fff;}\n\n\/* ---------- Filter bar ---------- *\/\n.filterbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:34px;padding:16px;background:#fff;border:1px solid var(--line);border-radius:var(--radius);}\n.filterbar input[type=search]{flex:1;min-width:180px;border:1px solid var(--line);border-radius:9px;padding:11px 14px;font-family:inherit;font-size:.95rem;}\n.chip{border:1px solid var(--line);background:#fff;padding:9px 16px;border-radius:24px;font-size:.85rem;font-weight:600;color:var(--text-soft);transition:.2s;}\n.chip.active,.chip:hover{border-color:var(--teal);color:var(--teal-dark);background:rgba(14,165,160,.08);}\n\n\/* ---------- Calculator cards (icon style, home grid) ---------- *\/\n.grid-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:18px;}\n.calc-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:22px;display:flex;flex-direction:column;gap:12px;transition:.2s;position:relative;overflow:hidden;}\n.calc-card:hover{box-shadow:var(--shadow-lg);transform:translateY(-3px);border-color:transparent;}\n.calc-card .icn{width:46px;height:46px;border-radius:11px;background:var(--ink);display:flex;align-items:center;justify-content:center;color:var(--teal);}\n.calc-card h4{font-size:1.02rem;margin:0;}\n.calc-card p{font-size:.87rem;color:var(--text-soft);margin:0;flex:1;}\n.calc-card .code{position:absolute;top:18px;right:18px;font-family:'IBM Plex Mono',monospace;font-size:.68rem;color:var(--text-soft);background:var(--bg);padding:3px 8px;border-radius:6px;}\n.calc-card .go{font-size:.85rem;font-weight:600;color:var(--teal-dark);display:inline-flex;align-items:center;gap:4px;}\n\n\/* ---------- Photo cards (listings) ---------- *\/\n.grid-photo{display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:24px;}\n.photo-card{background:#fff;border-radius:var(--radius);overflow:hidden;border:1px solid var(--line);transition:.2s;display:flex;flex-direction:column;}\n.photo-card:hover{box-shadow:var(--shadow-lg);transform:translateY(-3px);}\n.photo-card .thumb{position:relative;height:170px;overflow:hidden;background:var(--ink);}\n.photo-card .thumb img{width:100%;height:100%;object-fit:cover;transition:.4s;}\n.photo-card:hover .thumb img{transform:scale(1.06);}\n.photo-card .thumb .tag{position:absolute;top:12px;left:12px;background:rgba(15,28,46,.85);color:var(--amber);font-family:'IBM Plex Mono',monospace;font-size:.68rem;padding:4px 10px;border-radius:6px;letter-spacing:.03em;}\n.photo-card .body{padding:20px;display:flex;flex-direction:column;gap:10px;flex:1;}\n.photo-card .body p{color:var(--text-soft);font-size:.88rem;margin:0;flex:1;}\n.photo-card .foot{display:flex;justify-content:space-between;align-items:center;}\n\n\/* ---------- Category tabs on home ---------- *\/\n.tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:30px;}\n.tab{padding:11px 20px;border-radius:10px;background:#fff;border:1px solid var(--line);font-weight:600;font-size:.9rem;color:var(--text-soft);}\n.tab.active{background:var(--ink);color:#fff;border-color:var(--ink);}\n\n\/* ---------- Generic content blocks ---------- *\/\n.two-col{display:grid;grid-template-columns:1fr 1fr;gap:50px;align-items:center;}\n.card-plain{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:28px;}\n.value-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:20px;}\n.value-item{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:26px;}\n.value-item .n{font-family:'IBM Plex Mono',monospace;color:var(--amber);background:var(--ink);width:38px;height:38px;border-radius:9px;display:flex;align-items:center;justify-content:center;font-size:.85rem;margin-bottom:14px;}\n.timeline{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;}\n.timeline .step{position:relative;padding-top:20px;}\n.timeline .step::before{content:'';position:absolute;top:0;left:0;width:40px;height:3px;background:var(--teal);}\n.stats-strip{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;text-align:center;}\n.stats-strip b{display:block;font-family:'Space Grotesk',sans-serif;font-size:2.1rem;color:var(--amber);}\n.stats-strip span{color:rgba(255,255,255,.6);font-size:.85rem;}\nblockquote.testi{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:26px;font-size:1.02rem;color:var(--text);margin:0;}\nblockquote.testi footer{margin-top:14px;font-size:.85rem;color:var(--text-soft);font-style:normal;font-family:'IBM Plex Mono',monospace;}\n\n\/* ---------- Forms (contact + calculators) ---------- *\/\n.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px;}\n.field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px;}\n.field label{font-size:.85rem;font-weight:600;color:var(--ink);}\n.field small{color:var(--text-soft);font-size:.78rem;}\ninput[type=text],input[type=email],input[type=tel],input[type=number],input[type=date],input[type=time],input[type=search],select,textarea{\n  border:1px solid var(--line);border-radius:9px;padding:12px 14px;font-family:inherit;font-size:.95rem;background:#fff;color:var(--text);width:100%;\n}\ninput:focus,select:focus,textarea:focus{outline:none;border-color:var(--teal);box-shadow:0 0 0 3px rgba(14,165,160,.15);}\ntextarea{resize:vertical;min-height:110px;}\n.readout{margin-top:18px;background:var(--ink);border-radius:12px;padding:20px 22px;color:#fff;}\n.readout .r-label{font-family:'IBM Plex Mono',monospace;font-size:.72rem;letter-spacing:.08em;color:rgba(255,255,255,.5);text-transform:uppercase;margin-bottom:6px;}\n.readout .r-value{font-family:'IBM Plex Mono',monospace;font-size:1.9rem;color:var(--amber);font-weight:600;line-height:1.3;}\n.readout .r-sub{font-family:'IBM Plex Mono',monospace;font-size:.85rem;color:rgba(255,255,255,.72);margin-top:8px;display:flex;flex-wrap:wrap;gap:6px 18px;}\n.readout .r-sub b{color:#fff;}\n.readout.error{background:#3a1f1a;}\n.readout.error .r-value{color:#ff8f78;font-size:1.05rem;}\ntable.mini{width:100%;border-collapse:collapse;margin-top:14px;font-size:.85rem;}\ntable.mini th,table.mini td{padding:8px 10px;border-bottom:1px solid var(--line);text-align:right;}\ntable.mini th:first-child,table.mini td:first-child{text-align:left;}\ntable.mini thead th{color:var(--text-soft);font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.03em;}\n\n\/* ---------- Calculator page layout ---------- *\/\n.calc-layout{display:grid;grid-template-columns:2fr 1fr;gap:36px;align-items:start;}\n.breadcrumb{font-size:.85rem;color:var(--text-soft);margin-bottom:18px;}\n.breadcrumb a{color:var(--teal-dark);font-weight:600;}\n.calc-head{display:flex;gap:16px;align-items:center;margin-bottom:24px;}\n.calc-head .icn{width:56px;height:56px;border-radius:14px;background:var(--ink);display:flex;align-items:center;justify-content:center;color:var(--teal);flex:none;}\n.calc-panel{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:28px;}\n.side-panel{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:24px;}\n.side-panel h4{font-size:.95rem;margin-bottom:14px;}\n.related-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;}\n.related-list a{display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--line);font-size:.88rem;font-weight:600;color:var(--ink);}\n.related-list a:hover{color:var(--teal-dark);}\n.related-list a span{color:var(--text-soft);font-weight:400;font-size:.78rem;font-family:'IBM Plex Mono',monospace;}\n.ad-slot{margin-top:20px;border:1px dashed var(--line);border-radius:12px;padding:24px;text-align:center;color:var(--text-soft);font-size:.78rem;font-family:'IBM Plex Mono',monospace;background:#fbfbfc;}\n\n\/* ---------- Calculator detail article (how it works, formula, examples) ---------- *\/\n.calc-article{margin-top:48px;padding-top:40px;border-top:1px solid var(--line);max-width:820px;}\n.calc-article h2{font-size:1.5rem;margin-top:1.6em;}\n.calc-article h2:first-child{margin-top:0;}\n.calc-article h3{font-size:1.1rem;margin-top:1.4em;}\n.calc-article p{color:var(--text-soft);margin:0 0 1em;}\n.formula-box{background:var(--ink);color:#fff;border-radius:12px;padding:20px 22px;margin:16px 0;}\n.formula-box .f-label{font-family:'IBM Plex Mono',monospace;font-size:.7rem;letter-spacing:.08em;color:rgba(255,255,255,.5);text-transform:uppercase;margin-bottom:8px;}\n.formula-box .f-eq{font-family:'IBM Plex Mono',monospace;font-size:1.15rem;color:var(--amber);}\n.example-box{background:#fff;border:1px solid var(--line);border-left:4px solid var(--teal);border-radius:0 12px 12px 0;padding:18px 20px;margin:16px 0;}\n.example-box .e-label{font-family:'IBM Plex Mono',monospace;font-size:.7rem;letter-spacing:.08em;color:var(--teal-dark);text-transform:uppercase;margin-bottom:6px;}\n.ref-table{width:100%;border-collapse:collapse;margin:16px 0;font-size:.9rem;}\n.ref-table th,.ref-table td{padding:10px 14px;border-bottom:1px solid var(--line);text-align:left;}\n.ref-table thead th{background:var(--bg);color:var(--ink);font-weight:700;font-size:.78rem;text-transform:uppercase;letter-spacing:.03em;}\n.calc-article details{border:1px solid var(--line);border-radius:10px;padding:14px 18px;margin-bottom:10px;background:#fff;}\n.calc-article summary{font-weight:600;color:var(--ink);cursor:pointer;font-size:.95rem;}\n.calc-article details p{margin-top:10px;margin-bottom:0;}\n\n\/* ---------- Expanded calculator UI: charts, toggles, schedules ---------- *\/\n.chart-wrap{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:20px;margin-top:18px;}\n.chart-wrap h5{margin:0 0 14px;font-size:.9rem;color:var(--ink);}\n.chart-row{display:grid;grid-template-columns:1fr 1fr;gap:18px;}\n.chart-box{position:relative;height:220px;}\n.chart-row-centered{display:flex;justify-content:center;align-items:center;gap:40px;flex-wrap:wrap;padding:6px 0;}\n.chart-row-centered .chart-box{height:220px;width:220px;flex:none;display:flex;align-items:center;justify-content:center;}\n.chart-row-centered #m-legend{display:flex;flex-direction:column;justify-content:center;}\n@media(max-width:520px){.chart-row-centered{flex-direction:column;gap:22px;}}\n.adv-toggle{display:flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--line);border-radius:9px;padding:11px 14px;font-size:.88rem;font-weight:600;color:var(--ink);margin:6px 0 18px;cursor:pointer;user-select:none;}\n.adv-toggle input{width:16px;height:16px;accent-color:var(--teal);}\n.adv-panel{display:none;border-top:1px dashed var(--line);padding-top:14px;margin-top:2px;}\n.adv-panel.show{display:block;}\n.seg{display:flex;flex-wrap:wrap;background:var(--bg);border:1px solid var(--line);border-radius:9px;padding:3px;gap:3px;margin-bottom:14px;width:fit-content;}\n.seg button{border:0;background:transparent;padding:8px 16px;border-radius:7px;font-size:.82rem;font-weight:600;color:var(--text-soft);}\n.seg button.active{background:#fff;color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,.1);}\n.schedule-wrap{max-height:420px;overflow:auto;border:1px solid var(--line);border-radius:10px;margin-top:14px;}\n.schedule-wrap table{width:100%;}\n.schedule-wrap thead th{position:sticky;top:0;background:var(--ink);color:#fff;z-index:1;}\n.schedule-wrap tbody tr:nth-child(even){background:var(--bg);}\n.schedule-wrap tbody tr:hover{background:rgba(14,165,160,.08);}\n.schedule-wrap table.mini td,.schedule-wrap table.mini th{text-align:center;}\n.summary-mini{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px;margin-top:14px;}\n.summary-mini .m-item{background:var(--bg);border-radius:9px;padding:12px 14px;}\n.summary-mini .m-item span{display:block;font-size:.72rem;color:var(--text-soft);text-transform:uppercase;letter-spacing:.03em;margin-bottom:4px;}\n.summary-mini .m-item b{font-family:'IBM Plex Mono',monospace;font-size:1rem;color:var(--ink);}\n.legend-list{list-style:none;margin:14px 0 0;padding:0;font-size:.85rem;}\n.legend-list li{display:flex;align-items:center;gap:8px;padding:5px 0;}\n.legend-list .sw{width:11px;height:11px;border-radius:3px;flex:none;}\n.pay-callout{background:linear-gradient(135deg,#1E9E6B,#158a5a);color:#fff;border-radius:12px;padding:18px 22px;margin-top:18px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;}\n.pay-callout .pc-label{font-family:'IBM Plex Mono',monospace;font-size:.8rem;letter-spacing:.04em;opacity:.9;text-transform:uppercase;}\n.pay-callout .pc-value{font-family:'Space Grotesk',sans-serif;font-size:1.9rem;font-weight:700;}\n.kv-list{margin-top:16px;border:1px solid var(--line);border-radius:10px;overflow:hidden;}\n.kv-list .kv-row{display:flex;justify-content:space-between;padding:11px 16px;font-size:.9rem;border-bottom:1px solid var(--line);}\n.kv-list .kv-row:last-child{border-bottom:0;}\n.kv-list .kv-row:nth-child(even){background:var(--bg);}\n.kv-list .kv-row span{color:var(--text-soft);}\n.kv-list .kv-row b{font-family:'IBM Plex Mono',monospace;color:var(--ink);}\n.kv-list .kv-row.total{background:var(--ink);}\n.kv-list .kv-row.total span, .kv-list .kv-row.total b{color:#fff;}\n.unit-group{display:flex;gap:6px;}\n.unit-group input{flex:1;}\n.unit-group select{width:72px;flex:none;}\n.pay-table{width:100%;border-collapse:collapse;margin-top:16px;font-size:.9rem;}\n.pay-table th,.pay-table td{padding:10px 12px;border-bottom:1px solid var(--line);text-align:right;}\n.pay-table th:first-child,.pay-table td:first-child{text-align:left;}\n.pay-table thead th{background:var(--bg);font-size:.75rem;text-transform:uppercase;color:var(--text-soft);}\n.pay-table tr.total-row td{font-weight:700;color:var(--ink);border-top:2px solid var(--ink);}\n.more-link{color:var(--teal-dark);font-weight:600;font-size:.85rem;cursor:pointer;display:inline-block;margin-bottom:16px;}\n.section-tag{display:flex;align-items:center;gap:8px;margin:22px 0 14px;font-family:'IBM Plex Mono',monospace;font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:var(--teal-dark);font-weight:600;}\n.section-tag::before{content:'';width:18px;height:2px;background:var(--teal);display:inline-block;flex:none;}\n.more-panel-card{background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:18px 18px 4px;margin-top:6px;}\n.badge-popular{display:inline-flex;align-items:center;gap:6px;background:linear-gradient(135deg,var(--amber),#e0921a);color:var(--ink);font-weight:700;font-size:.72rem;padding:6px 13px;border-radius:20px;margin-bottom:18px;font-family:'IBM Plex Mono',monospace;letter-spacing:.03em;}\n.rate-box{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:22px 24px;margin-top:24px;box-shadow:var(--shadow);}\n.rate-box .rb-title{font-family:'Space Grotesk',sans-serif;font-weight:700;color:var(--ink);margin-bottom:14px;font-size:1rem;}\n.rate-row{display:flex;gap:26px;flex-wrap:wrap;margin-bottom:18px;}\n.rate-item{font-size:.9rem;color:var(--text-soft);}\n.rate-item b{color:var(--teal-dark);font-weight:700;margin-left:5px;text-decoration:underline;text-underline-offset:2px;}\n.rate-box .rb-note{font-size:.74rem;color:var(--text-soft);margin-bottom:16px;}\n.rate-box .rb-actions{display:flex;gap:10px;flex-wrap:wrap;}\n.rate-box .rb-actions .btn{background:var(--ink);color:#fff;border-color:var(--ink);}\n.rate-box .rb-actions .btn:hover{background:var(--ink-3);}\n.extra-row{display:flex;gap:8px;align-items:center;}\n.extra-row input[type=number]{flex:1;}\n.extra-row span{color:var(--text-soft);font-size:.8rem;white-space:nowrap;}\n.extra-row select{max-width:82px;}\n.extra-row input[type=number]#m-ex-mo-start-y,.extra-row input[type=number]#m-ex-yr-start-y,.extra-row input[type=number]#m-ex-one-date-y{max-width:82px;}\n.biweekly-box{background:#fff;border:1px solid var(--teal);border-left:4px solid var(--teal);border-radius:0 12px 12px 0;padding:18px 20px;margin-top:16px;}\n.biweekly-box h5{margin:0 0 10px;color:var(--teal-dark);font-size:.9rem;}\n.calc-section{background:#fff;border-radius:12px;padding:20px 22px;margin-top:20px;border:1px solid var(--line);border-left:4px solid transparent;box-shadow:0 2px 6px rgba(15,28,46,.05);}\n.calc-section-head{display:flex;align-items:center;gap:10px;margin-bottom:14px;}\n.calc-section-head .cs-icn{width:30px;height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex:none;color:#fff;}\n.calc-section-head h5{margin:0;font-size:.95rem;}\n.section-blue{border-left-color:#4C6FFF;}\n.section-blue .cs-icn{background:#4C6FFF;}\n.section-green{border-left-color:#1E9E6B;}\n.section-green .cs-icn{background:#1E9E6B;}\n.section-amber{border-left-color:#F5A623;}\n.section-amber .cs-icn{background:#F5A623;}\n.section-navy{border-left-color:var(--ink);}\n.section-navy .cs-icn{background:var(--ink);}\n.mortgage-shell{background:#F3F6FC;border-radius:var(--radius);margin:-28px;padding:28px;}\n@media(max-width:700px){.chart-row{grid-template-columns:1fr;}}\n.calc-set{display:flex;flex-direction:column;gap:0;}\n.inline-fields{display:flex;gap:10px;flex-wrap:wrap;align-items:end;}\n.inline-fields .field{flex:1;min-width:110px;}\n.row-add{display:flex;gap:10px;margin-bottom:10px;align-items:center;}\n.row-add input,.row-add select{flex:1;}\n.row-add button{flex:none;}\n.tag-btn{border:1px solid var(--line);background:#fff;padding:9px 14px;border-radius:8px;font-size:.85rem;font-weight:600;color:var(--text-soft);}\n.tag-btn.active{background:var(--ink);color:#fff;border-color:var(--ink);}\n.pw-display{font-family:'IBM Plex Mono',monospace;font-size:1.1rem;background:var(--ink);color:var(--amber);padding:14px 16px;border-radius:9px;word-break:break-all;}\n.pw-terminal{background:linear-gradient(155deg,#0D1826,#122238);border:1px solid #1E3350;border-radius:12px;padding:22px;position:relative;overflow:hidden;margin-top:22px;}\n.pw-terminal::before{content:'\u25cf \u25cf \u25cf';position:absolute;top:10px;left:14px;color:rgba(255,255,255,.25);font-size:.7rem;letter-spacing:3px;}\n.pw-terminal .pw-value{font-family:'IBM Plex Mono',monospace;font-size:1.35rem;color:#3DFFC4;word-break:break-all;margin-top:18px;text-shadow:0 0 12px rgba(61,255,196,.35);line-height:1.5;}\n.pw-actions{display:flex;gap:10px;margin-top:16px;flex-wrap:wrap;}\n.strength-wrap{margin-top:18px;}\n.strength-label{display:flex;justify-content:space-between;font-size:.85rem;margin-bottom:6px;}\n.strength-bar{height:10px;background:rgba(255,255,255,.1);border-radius:20px;overflow:hidden;}\n.strength-fill{height:100%;border-radius:20px;transition:width .3s ease;}\n.dist-row{display:flex;align-items:center;gap:10px;margin-bottom:8px;font-size:.82rem;}\n.dist-row .dist-label{width:90px;color:var(--text-soft);flex:none;}\n.dist-track{flex:1;height:8px;background:var(--line);border-radius:20px;overflow:hidden;}\n.dist-fill{height:100%;border-radius:20px;}\n.dist-row .dist-pct{width:40px;text-align:right;font-family:'IBM Plex Mono',monospace;color:var(--text-soft);flex:none;}\n.privacy-note{display:flex;align-items:center;gap:8px;font-size:.8rem;color:var(--text-soft);margin-top:14px;padding:10px 12px;background:var(--bg);border-radius:8px;}\n.quiz-q{margin-bottom:16px;}\n.quiz-q p{font-weight:600;font-size:.9rem;margin-bottom:8px;color:var(--ink);}\n.quiz-opts{display:flex;gap:8px;flex-wrap:wrap;}\n.quiz-opt{border:1px solid var(--line);background:#fff;padding:8px 14px;border-radius:8px;font-size:.82rem;cursor:pointer;}\n.quiz-opt.active{background:var(--ink);color:#fff;border-color:var(--ink);}\n.weak-pw-table td:first-child{font-family:'IBM Plex Mono',monospace;color:var(--danger);}\n.dd-timeline{position:relative;height:70px;margin:24px 0 10px;}\n.dd-timeline .track{position:absolute;top:32px;left:0;right:0;height:6px;background:var(--line);border-radius:4px;}\n.dd-timeline .fill{position:absolute;top:32px;height:6px;background:linear-gradient(90deg,var(--teal),var(--amber));border-radius:4px;}\n.dd-timeline .pt{position:absolute;top:24px;width:22px;height:22px;border-radius:50%;background:var(--ink);border:3px solid #fff;box-shadow:0 0 0 2px var(--ink);transform:translateX(-50%);}\n.dd-timeline .pt-label{position:absolute;top:0;font-size:.7rem;font-family:'IBM Plex Mono',monospace;color:var(--text-soft);white-space:nowrap;transform:translateX(-50%);}\n.dd-timeline .pt-label.bottom{top:46px;}\n.dd-cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;margin-top:10px;}\n.dd-cal-grid .dow{font-size:.68rem;color:var(--text-soft);text-align:center;font-weight:700;padding-bottom:4px;}\n.dd-cal-grid .day{aspect-ratio:1;display:flex;align-items:center;justify-content:center;font-size:.75rem;border-radius:6px;background:var(--bg);color:var(--text-soft);}\n.dd-cal-grid .day.in-range{background:rgba(14,165,160,.18);color:var(--ink);font-weight:600;}\n.dd-cal-grid .day.weekend{background:rgba(224,83,61,.12);}\n.dd-cal-grid .day.weekend.in-range{background:rgba(224,83,61,.28);}\n.dd-cal-grid .day.empty{background:transparent;}\n.dd-cal-label{font-size:.78rem;font-weight:700;color:var(--ink);margin:14px 0 4px;}\n.pw-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#FAFCFF;box-shadow:0 0 0 4px rgba(14,165,160,.07);}\n.age-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.dd-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.dd-main-result{background:var(--ink);color:#fff;border-radius:12px;padding:32px 24px;text-align:center;margin-top:22px;}\n.gpa-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.grade-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.uc-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.bmi-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.bmr-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.cal-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.bf-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.iw-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.iw-dist-wrap{position:relative;height:80px;margin:18px 0 30px;}\n.iw-dist-wrap .track{position:absolute;top:34px;left:0;right:0;height:14px;background:var(--line);border-radius:8px;}\n.iw-dist-wrap .range-band{position:absolute;top:34px;height:14px;background:rgba(30,158,107,.35);border-radius:8px;}\n.iw-dist-wrap .fpt{position:absolute;top:30px;width:10px;height:10px;border-radius:50%;background:var(--teal-dark);transform:translateX(-50%);}\n.iw-dist-wrap .cpt{position:absolute;top:24px;width:16px;height:16px;border-radius:50%;background:var(--ink);border:3px solid #fff;box-shadow:0 0 0 2px var(--ink);transform:translateX(-50%);}\n.iw-dist-wrap .tpt{position:absolute;top:24px;width:16px;height:16px;border-radius:50%;background:var(--amber);border:3px solid #fff;box-shadow:0 0 0 2px var(--amber);transform:translateX(-50%);}\n.iw-dist-wrap .dlabel{position:absolute;top:52px;font-size:.65rem;font-family:'IBM Plex Mono',monospace;color:var(--text-soft);white-space:nowrap;transform:translateX(-50%);}\n.iw-dist-legend{display:flex;gap:16px;flex-wrap:wrap;font-size:.72rem;color:var(--text-soft);margin-top:6px;}\n.iw-dist-legend span{display:flex;align-items:center;gap:5px;}\n.iw-dist-legend .sw{width:10px;height:10px;border-radius:50%;display:inline-block;}\n.pg-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.pc-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.fr-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.av-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.sd-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.rn-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.tr-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.tr-coords-row{display:flex;gap:8px;flex-wrap:nowrap;align-items:end;overflow-x:auto;padding-bottom:4px;}\n.tr-coords-row .field{flex:0 0 auto;min-width:64px;margin-bottom:0;}\n.tr-coords-row .field label{white-space:nowrap;font-size:.78rem;}\n.tr-coords-row .field input{width:64px;padding:10px 8px;}\n.rn-preset-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:10px;}\n.rn-preset-btn{background:#fff;border:1px solid var(--line);border-radius:10px;padding:14px 10px;text-align:center;cursor:pointer;font-size:.85rem;font-weight:600;color:var(--ink);}\n.rn-preset-btn:hover{border-color:var(--teal);color:var(--teal-dark);}\n.rn-preset-btn .icn{font-size:1.6rem;display:block;margin-bottom:6px;}\n.rn-color-swatch{width:100%;height:90px;border-radius:12px;border:1px solid var(--line);margin-bottom:12px;}\n.rn-die{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px;background:#fff;border:2px solid var(--ink);border-radius:10px;font-family:'IBM Plex Mono',monospace;font-weight:700;font-size:1.3rem;margin:4px;}\n.qd-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.ln-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.al-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.si-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.ci-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.rt-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.iv-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.am-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.st-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.tp-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.dc-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.dc-stage-row{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:center;margin-bottom:8px;}\n.dc-compare-col{border:1px solid var(--line);border-radius:10px;padding:14px;background:#fff;}\n.dc-compare-col.best{border-color:#1E9E6B;box-shadow:0 0 0 2px rgba(30,158,107,.15);}\n.tp-quick-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:14px;}\n.tp-quick-btn{background:#fff;border:2px solid var(--line);border-radius:10px;padding:14px 4px;text-align:center;font-weight:700;font-size:1rem;cursor:pointer;color:var(--ink);}\n.tp-quick-btn.active{border-color:var(--teal);background:#0EA5A014;color:var(--teal-dark);}\n.tp-person-row{display:grid;grid-template-columns:1.4fr 1fr auto;gap:8px;align-items:center;margin-bottom:8px;}\n.st-cart-row{display:grid;grid-template-columns:1.6fr .8fr .6fr .6fr auto;gap:8px;align-items:center;margin-bottom:8px;}\n.st-cart-row input{width:100%;}\n.st-receipt{background:#fff;border:1px solid var(--line);border-radius:10px;padding:16px;font-family:'IBM Plex Mono',monospace;font-size:.85rem;}\n.st-receipt .rline{display:flex;justify-content:space-between;padding:4px 0;}\n.st-receipt .rline.total{border-top:1px dashed var(--line);margin-top:8px;padding-top:8px;font-weight:700;}\n.rt-gauge-wrap{text-align:center;margin:10px 0;}\n.rt-gauge-label{display:inline-block;padding:8px 20px;border-radius:20px;font-weight:700;font-size:1rem;margin-top:8px;}\n.ln-example-chip{display:inline-block;background:#fff;border:1px solid var(--line);border-radius:20px;padding:8px 16px;font-size:.82rem;margin:0 6px 8px 0;cursor:pointer;}\n.ln-example-chip:hover{border-color:var(--teal);color:var(--teal-dark);}\n.ln-compare-table th{background:var(--bg);}\n.ln-compare-table td.better{color:#1E9E6B;font-weight:700;}\n.sc-shell{border:2px solid var(--teal);border-radius:16px;padding:26px;margin:-28px;background:#F7FEFD;box-shadow:0 0 0 4px rgba(14,165,160,.08);}\n.sc-display{background:var(--ink);border-radius:10px;padding:16px 18px;margin-bottom:14px;}\n.sc-display input{width:100%;background:transparent;border:0;color:#fff;font-family:'IBM Plex Mono',monospace;font-size:1.6rem;text-align:right;padding:0;}\n.sc-display input:focus{outline:none;box-shadow:none;}\n.sc-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;}\n.sc-grid button{border:1px solid var(--line);border-radius:8px;padding:12px 4px;font-family:'IBM Plex Mono',monospace;font-size:.92rem;font-weight:600;color:var(--ink);cursor:pointer;background:#EAF7F6;transition:.15s;}\n.sc-grid button:hover{background:var(--teal);color:#fff;border-color:var(--teal);}\n.sc-grid button.sc-op{background:#fff;color:var(--teal-dark);}\n.sc-grid button.sc-util{background:#fff;color:var(--text-soft);}\n.sc-grid button.sc-eq{background:var(--teal);color:#fff;}\n.sc-grid button.sc-eq:hover{background:var(--teal-dark);}\n.sc-angle-cell{grid-column:span 2;display:flex;align-items:center;justify-content:center;gap:12px;border:1px solid var(--line);border-radius:8px;background:#fff;font-size:.82rem;font-weight:600;color:var(--ink);}\n.sc-angle-cell label{display:flex;align-items:center;gap:4px;cursor:pointer;}\n.sc-angle-cell input[type=radio]{accent-color:var(--teal);width:15px;height:15px;}\n@media(max-width:480px){.sc-grid button{font-size:.78rem;padding:10px 2px;} .sc-display input{font-size:1.25rem;}}\n.sc-history-item{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:.85rem;cursor:pointer;font-family:'IBM Plex Mono',monospace;}\n.sc-history-item:hover{color:var(--teal-dark);}\n.sc-mem-badge{display:inline-block;background:var(--amber);color:var(--ink);font-size:.68rem;font-weight:700;padding:2px 8px;border-radius:10px;margin-left:8px;}\n.stat-bar-chart{display:flex;align-items:flex-end;gap:4px;height:160px;padding:10px 0;overflow-x:auto;position:relative;}\n.stat-bar-chart .bar-col{flex:0 0 auto;min-width:18px;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;position:relative;}\n.stat-bar-chart .bar{width:16px;border-radius:3px 3px 0 0;background:var(--teal);min-height:2px;}\n.stat-bar-chart .bar.outlier{background:var(--danger);}\n.stat-bar-chart .mean-line{position:absolute;left:0;right:0;border-top:2px dashed var(--amber);z-index:2;}\n.boxplot-wrap{position:relative;height:70px;margin:20px 10px;}\n.boxplot-wrap .bp-line{position:absolute;top:35px;height:2px;background:var(--line);}\n.boxplot-wrap .bp-box{position:absolute;top:18px;height:34px;background:rgba(14,165,160,.25);border:2px solid var(--teal);border-radius:4px;}\n.boxplot-wrap .bp-median{position:absolute;top:18px;height:34px;width:2px;background:var(--ink);}\n.boxplot-wrap .bp-whisker{position:absolute;top:25px;height:20px;width:2px;background:var(--text-soft);}\n.boxplot-wrap .bp-label{position:absolute;top:56px;font-size:.65rem;color:var(--text-soft);font-family:'IBM Plex Mono',monospace;transform:translateX(-50%);}\n.pc-bar-wrap{background:var(--line);border-radius:20px;height:28px;overflow:hidden;margin:14px 0;position:relative;}\n.pc-bar-fill{height:100%;background:linear-gradient(90deg,var(--teal),var(--amber));border-radius:20px;display:flex;align-items:center;justify-content:flex-end;padding-right:10px;color:#fff;font-size:.78rem;font-weight:700;font-family:'IBM Plex Mono',monospace;transition:width .3s ease;}\n.pc-example-chip{display:inline-block;background:#fff;border:1px solid var(--line);border-radius:20px;padding:8px 16px;font-size:.82rem;margin:0 6px 8px 0;cursor:pointer;}\n.pc-example-chip:hover{border-color:var(--teal);color:var(--teal-dark);}\n.fr-visual-row{display:flex;gap:24px;flex-wrap:wrap;align-items:center;justify-content:center;margin:18px 0;}\n.fr-numline{position:relative;height:50px;margin:20px 10px;}\n.fr-numline .line{position:absolute;top:20px;left:0;right:0;height:3px;background:var(--line);}\n.fr-numline .tick{position:absolute;top:14px;width:2px;height:16px;background:var(--text-soft);}\n.fr-numline .ticklabel{position:absolute;top:32px;font-size:.68rem;color:var(--text-soft);font-family:'IBM Plex Mono',monospace;transform:translateX(-50%);}\n.fr-numline .marker{position:absolute;top:10px;width:14px;height:14px;border-radius:50%;background:var(--amber);border:2px solid #fff;box-shadow:0 0 0 2px var(--ink);transform:translateX(-50%);}\n.pg-timeline{position:relative;height:70px;margin:20px 0 10px;}\n.pg-fruit-box{display:flex;align-items:center;gap:16px;background:#fff;border:1px solid var(--line);border-radius:12px;padding:16px;}\n.pg-fruit-box .emoji{font-size:2.6rem;line-height:1;}\n.pg-checklist-item{display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid var(--line);font-size:.88rem;}\n.pg-checklist-item input{width:17px;height:17px;accent-color:var(--teal);}\n.pg-timer-display{font-family:'IBM Plex Mono',monospace;font-size:2.2rem;text-align:center;color:var(--ink);background:var(--bg);border-radius:12px;padding:20px;margin:14px 0;}\n.cal-deficit-bar{position:relative;height:40px;background:var(--line);border-radius:8px;overflow:hidden;margin:14px 0;}\n.cal-deficit-fill{position:absolute;top:0;left:0;height:100%;border-radius:8px;display:flex;align-items:center;padding-left:10px;color:#fff;font-size:.78rem;font-weight:600;font-family:'IBM Plex Mono',monospace;}\n.cal-meal-row{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid var(--line);}\n.cal-meal-row .meal-bar{flex:1;height:8px;background:var(--line);border-radius:20px;margin:0 12px;overflow:hidden;}\n.cal-meal-row .meal-fill{height:100%;background:var(--teal);border-radius:20px;}\n.bmi-scale-wrap{margin:18px 0;}\n.bmi-scale-labels{display:flex;justify-content:space-between;font-size:.68rem;color:var(--text-soft);margin-top:6px;}\n.uc-results-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;}\n.uc-result-card{background:#fff;border:1px solid var(--line);border-radius:10px;padding:12px 14px;}\n.uc-result-card .uc-r-unit{font-size:.72rem;color:var(--text-soft);text-transform:uppercase;letter-spacing:.03em;}\n.uc-result-card .uc-r-val{font-family:'IBM Plex Mono',monospace;font-size:1rem;color:var(--ink);font-weight:600;margin-top:2px;}\n.uc-chip-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px;}\n.uc-swap-btn{width:42px;height:42px;border-radius:50%;border:1px solid var(--line);background:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none;font-size:1.1rem;color:var(--teal-dark);}\n.uc-swap-btn:hover{background:var(--bg);}\n.uc-from-to-row{display:flex;gap:10px;align-items:flex-end;margin-bottom:16px;}\n.uc-from-to-row .field{flex:1;margin-bottom:0;}\n.uc-recent-item{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:.85rem;cursor:pointer;}\n.uc-recent-item:hover{color:var(--teal-dark);}\n.gpa-progress{background:var(--line);border-radius:20px;height:14px;overflow:hidden;position:relative;}\n.gpa-progress-fill{height:100%;background:linear-gradient(90deg,var(--teal),var(--amber));border-radius:20px;transition:width .3s ease;}\n.gpa-course-row{display:grid;grid-template-columns:1.6fr 1fr 1fr 0.8fr 1fr auto auto auto;gap:8px;align-items:center;margin-bottom:10px;}\n.gpa-course-row input,.gpa-course-row select{width:100%;}\n.gpa-course-row .gpa-mini-toggle{display:flex;align-items:center;gap:4px;font-size:.72rem;color:var(--text-soft);white-space:nowrap;}\n.gpa-bar-chart{display:flex;align-items:flex-end;gap:8px;height:160px;padding:10px 0;}\n.gpa-bar-chart .bar-col{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;}\n.gpa-bar-chart .bar{width:70%;border-radius:6px 6px 0 0;background:var(--teal);position:relative;min-height:2px;}\n.gpa-bar-chart .bar-val{font-size:.68rem;color:var(--text-soft);margin-bottom:4px;font-family:'IBM Plex Mono',monospace;}\n.gpa-bar-chart .bar-label{font-size:.68rem;color:var(--text-soft);margin-top:6px;text-align:center;}\n.gpa-insight{border-radius:12px;padding:18px 20px;margin-top:16px;font-size:.9rem;}\n@media(max-width:760px){.gpa-course-row{grid-template-columns:1fr 1fr;}}\n.dd-main-value{font-family:'Space Grotesk',sans-serif;font-size:1.9rem;font-weight:700;color:var(--amber);line-height:1.3;}\n.dd-main-sub{margin-top:12px;font-size:.85rem;color:rgba(255,255,255,.65);}\n\n@media(max-width:900px){\n  .hero-grid{grid-template-columns:1fr;}\n  .hero h1{font-size:2.2rem;}\n  .device-wrap{order:-1;max-width:280px;margin:0 auto;}\n  .two-col{grid-template-columns:1fr;}\n  .timeline{grid-template-columns:repeat(2,1fr);}\n  .stats-strip{grid-template-columns:repeat(2,1fr);}\n  .calc-layout{grid-template-columns:1fr;}\n  .form-row{grid-template-columns:1fr;}\n}\n\n\/* ---------- Footer ---------- *\/\nfooter{background:var(--ink);color:rgba(255,255,255,.65);padding:60px 0 0;}\n.foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:36px;padding-bottom:44px;}\n.foot-grid h5{color:#fff;font-family:'Space Grotesk',sans-serif;font-size:.95rem;margin-bottom:16px;}\n.foot-grid ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px;font-size:.9rem;}\n.foot-grid ul a:hover{color:#fff;}\n.foot-bottom{border-top:1px solid rgba(255,255,255,.1);padding:20px 0;display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;font-size:.82rem;}\n.social-row{display:flex;gap:10px;margin-top:16px;}\n.social-row a{width:36px;height:36px;border-radius:9px;background:rgba(255,255,255,.08);display:flex;align-items:center;justify-content:center;color:#fff;}\n.social-row a:hover{background:var(--teal);}\n@media(max-width:760px){.foot-grid{grid-template-columns:1fr 1fr;}}\n\n\/* misc *\/\n.map-frame{border:0;width:100%;height:320px;border-radius:var(--radius);filter:grayscale(.15);}\n.contact-info-item{display:flex;gap:14px;align-items:flex-start;margin-bottom:22px;}\n.contact-info-item .icn{width:42px;height:42px;border-radius:10px;background:var(--ink);color:var(--teal);display:flex;align-items:center;justify-content:center;flex:none;}\n.contact-info-item h5{margin:0 0 4px;font-size:.95rem;color:var(--ink);}\n.contact-info-item p{margin:0;color:var(--text-soft);font-size:.9rem;}\n.notice{padding:14px 16px;border-radius:10px;background:rgba(30,158,107,.1);border:1px solid rgba(30,158,107,.35);color:#0f6a45;font-size:.9rem;margin-top:16px;display:none;}\n.notice.show{display:block;}\n.badge-cat{display:inline-block;font-family:'IBM Plex Mono',monospace;font-size:.68rem;padding:4px 9px;border-radius:6px;background:var(--bg);color:var(--text-soft);margin-left:8px;}\n<\/style>\n<\/head>\n<body>\n\n<header id=\"siteHeader\">\n  <div class=\"wrap nav\">\n    <a href=\"#\/home\" class=\"logo\" aria-label=\"MAFtrix home\"><span class=\"logo-mark\">M<\/span><span class=\"logo-word\">MAF<span class=\"accent\">trix<\/span><\/span><\/a>\n    <nav class=\"links\" id=\"navLinks\" aria-label=\"Main navigation\">\n      <a href=\"#\/home\" data-route=\"home\">Home<\/a>\n      <a href=\"#\/about\" data-route=\"about\">About<\/a>\n      <a href=\"#\/services\" data-route=\"services\">Services<\/a>\n      <a href=\"#\/listings\" data-route=\"listings\">Listings<\/a>\n      <a href=\"#\/contact\" data-route=\"contact\">Contact<\/a>\n    <\/nav>\n    <button class=\"burger\" id=\"burgerBtn\" aria-label=\"Toggle menu\" aria-expanded=\"false\"><span><\/span><span><\/span><span><\/span><\/button>\n  <\/div>\n<\/header>\n\n<main>\n\n<!-- ============ HOME ============ -->\n<section class=\"page\" id=\"page-home\" data-page=\"home\">\n  <div class=\"hero\">\n    <div class=\"wrap hero-grid\">\n      <div>\n        <span class=\"eyebrow\">\u25cf 31 FREE CALCULATORS \u2014 INSTANT RESULTS<\/span>\n        <h1>Calculate anything.<br>Get the <em>right<\/em> answer, instantly.<\/h1>\n        <p class=\"lead\">MAFtrix is a free online calculator hub for math, finance, health and everyday life \u2014 no sign-up, no downloads, just accurate answers in seconds.<\/p>\n        <form class=\"search-bar\" id=\"heroSearch\" role=\"search\">\n          <label for=\"heroSearchInput\" class=\"sr-only\" style=\"position:absolute;left:-9999px;\">Search calculators<\/label>\n          <input type=\"search\" id=\"heroSearchInput\" placeholder=\"Search a calculator \u2014 e.g. mortgage, BMI, GPA\u2026\">\n          <button class=\"btn btn-primary\" type=\"submit\">Search<\/button>\n        <\/form>\n        <div class=\"hero-actions\">\n          <a href=\"#\/calculator\/mortgage\" class=\"btn btn-amber\">Try Mortgage Calculator<\/a>\n          <a href=\"#\/listings\" class=\"btn btn-outline\">Browse All Calculators<\/a>\n        <\/div>\n        <div class=\"stat-row\">\n          <div class=\"stat\"><b>31+<\/b><span>Calculators<\/span><\/div>\n          <div class=\"stat\"><b>100%<\/b><span>Free to use<\/span><\/div>\n          <div class=\"stat\"><b>0s<\/b><span>Sign-up required<\/span><\/div>\n        <\/div>\n      <\/div>\n      <div class=\"device-wrap\" aria-hidden=\"true\">\n        <svg viewBox=\"0 0 320 380\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n          <ellipse cx=\"160\" cy=\"360\" rx=\"120\" ry=\"14\" fill=\"#000\" opacity=\".25\"\/>\n          <rect x=\"30\" y=\"20\" width=\"260\" height=\"320\" rx=\"26\" fill=\"#16273D\" stroke=\"#243c5c\" stroke-width=\"2\"\/>\n          <rect x=\"52\" y=\"46\" width=\"216\" height=\"72\" rx=\"10\" fill=\"#0B131F\"\/>\n          <text x=\"248\" y=\"96\" text-anchor=\"end\" font-family=\"IBM Plex Mono, monospace\" font-size=\"30\" fill=\"#F5A623\">128.45<\/text>\n          <text x=\"64\" y=\"70\" font-family=\"IBM Plex Mono, monospace\" font-size=\"12\" fill=\"#5B7A93\">TOTAL RESULT<\/text>\n          <g font-family=\"IBM Plex Mono, monospace\" font-size=\"15\" fill=\"#fff\">\n            <!-- button grid -->\n            <g>\n              <rect x=\"52\" y=\"134\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"75\" y=\"159\" text-anchor=\"middle\">7<\/text>\n              <rect x=\"106\" y=\"134\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"129\" y=\"159\" text-anchor=\"middle\">8<\/text>\n              <rect x=\"160\" y=\"134\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"183\" y=\"159\" text-anchor=\"middle\">9<\/text>\n              <rect x=\"214\" y=\"134\" width=\"54\" height=\"40\" rx=\"9\" fill=\"#0EA5A0\"\/><text x=\"241\" y=\"159\" text-anchor=\"middle\">\u00f7<\/text>\n\n              <rect x=\"52\" y=\"182\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"75\" y=\"207\" text-anchor=\"middle\">4<\/text>\n              <rect x=\"106\" y=\"182\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"129\" y=\"207\" text-anchor=\"middle\">5<\/text>\n              <rect x=\"160\" y=\"182\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"183\" y=\"207\" text-anchor=\"middle\">6<\/text>\n              <rect x=\"214\" y=\"182\" width=\"54\" height=\"40\" rx=\"9\" fill=\"#0EA5A0\"\/><text x=\"241\" y=\"207\" text-anchor=\"middle\">\u00d7<\/text>\n\n              <rect x=\"52\" y=\"230\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"75\" y=\"255\" text-anchor=\"middle\">1<\/text>\n              <rect x=\"106\" y=\"230\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"129\" y=\"255\" text-anchor=\"middle\">2<\/text>\n              <rect x=\"160\" y=\"230\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"183\" y=\"255\" text-anchor=\"middle\">3<\/text>\n              <rect x=\"214\" y=\"230\" width=\"54\" height=\"40\" rx=\"9\" fill=\"#0EA5A0\"\/><text x=\"241\" y=\"255\" text-anchor=\"middle\">\u2212<\/text>\n\n              <rect x=\"52\" y=\"278\" width=\"100\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"102\" y=\"303\" text-anchor=\"middle\">0<\/text>\n              <rect x=\"160\" y=\"278\" width=\"46\" height=\"40\" rx=\"9\" fill=\"#1E3350\"\/><text x=\"183\" y=\"303\" text-anchor=\"middle\">.<\/text>\n              <rect x=\"214\" y=\"278\" width=\"54\" height=\"40\" rx=\"9\" fill=\"#F5A623\"\/><text x=\"241\" y=\"303\" text-anchor=\"middle\" fill=\"#0F1C2E\">+<\/text>\n            <\/g>\n          <\/g>\n        <\/svg>\n      <\/div>\n    <\/div>\n  <\/div>\n  <div class=\"ticks dark\"><\/div>\n\n  <div class=\"section bg-alt\">\n    <div class=\"wrap\">\n      <div class=\"section-head\">\n        <span class=\"kicker\">\/\/ Browse by category<\/span>\n        <h2>Every calculator you need, in one place<\/h2>\n        <p>Filter by category or search to jump straight to the tool you&#8217;re after. Every calculator opens on its own page with a clear result readout.<\/p>\n      <\/div>\n      <div class=\"tabs\" id=\"homeTabs\"><\/div>\n      <div class=\"grid-cards\" id=\"homeGrid\"><\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"section on-dark\">\n    <div class=\"wrap stats-strip\">\n      <div><b>31+<\/b><span>Calculators live<\/span><\/div>\n      <div><b>4<\/b><span>Categories covered<\/span><\/div>\n      <div><b>190+<\/b><span>Countries reached<\/span><\/div>\n      <div><b>&lt;1s<\/b><span>Average calc time<\/span><\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"section\">\n    <div class=\"wrap two-col\">\n      <div>\n        <span class=\"kicker\">\/\/ Why MAFtrix<\/span>\n        <h2>Built for speed, trusted for accuracy<\/h2>\n        <p style=\"color:var(--text-soft);margin-bottom:22px;\">Every formula on MAFtrix follows the same standards used by financial institutions and health professionals \u2014 mortgage math, BMI classifications, and statistical formulas are all industry-standard.<\/p>\n        <div class=\"value-grid\">\n          <div class=\"value-item\"><div class=\"n\">01<\/div><h4>Instant<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;margin:0;\">Results update the moment you calculate \u2014 no page reloads.<\/p><\/div>\n          <div class=\"value-item\"><div class=\"n\">02<\/div><h4>Private<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;margin:0;\">Calculations run in your browser. Nothing you enter is stored.<\/p><\/div>\n        <\/div>\n      <\/div>\n      <div class=\"card-plain\">\n        <blockquote class=\"testi\">&#8220;I use MAFtrix&#8217;s mortgage and loan calculators every week \u2014 clean layout, and it never gets in the way of the numbers.&#8221;<footer>\u2014 R. Delacroix, Toronto, CA<\/footer><\/blockquote>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- ============ ABOUT ============ -->\n<section class=\"page\" id=\"page-about\" data-page=\"about\">\n  <div class=\"section\" style=\"padding-top:56px;\">\n    <div class=\"wrap\">\n      <div class=\"section-head\">\n        <span class=\"kicker\">\/\/ About MAFtrix<\/span>\n        <h1>Calculators built for clarity, not clutter<\/h1>\n        <p>MAFtrix started with a simple frustration: most calculator sites bury a simple answer under ads, pop-ups and cluttered design. We set out to build something faster, cleaner, and genuinely pleasant to use.<\/p>\n      <\/div>\n      <div class=\"two-col\" style=\"margin-bottom:70px;\">\n        <div>\n          <h2>Our story<\/h2>\n          <p style=\"color:var(--text-soft);\">MAFtrix was created as a single home for the everyday calculations people search for \u2014 from mortgage payments to BMI, GPA to standard deviation. Instead of one generic tool trying to do everything, every calculator on MAFtrix is purpose-built for its exact question, with a clear, instrument-style result readout so you always know exactly what you&#8217;re looking at.<\/p>\n          <p style=\"color:var(--text-soft);\">Today, MAFtrix serves visitors from the United States, Canada, the United Kingdom, France, Australia and beyond \u2014 anyone who wants a fast, free, no-nonsense answer.<\/p>\n        <\/div>\n        <div class=\"card-plain\">\n          <h4>At a glance<\/h4>\n          <table class=\"mini\">\n            <tr><td>Calculators live<\/td><td>31+<\/td><\/tr>\n            <tr><td>Categories<\/td><td>Financial, Math, Health, Everyday<\/td><\/tr>\n            <tr><td>Cost to use<\/td><td>Free, always<\/td><\/tr>\n            <tr><td>Account required<\/td><td>None<\/td><\/tr>\n            <tr><td>Primary regions<\/td><td>US, CA, UK, FR, AU<\/td><\/tr>\n          <\/table>\n        <\/div>\n      <\/div>\n\n      <div class=\"section-head\">\n        <span class=\"kicker\">\/\/ What guides us<\/span>\n        <h2>Our principles<\/h2>\n      <\/div>\n      <div class=\"value-grid\" style=\"margin-bottom:70px;\">\n        <div class=\"value-item\"><div class=\"n\">01<\/div><h4>Accuracy first<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">Every formula is checked against standard, published methods \u2014 nothing is guessed.<\/p><\/div>\n        <div class=\"value-item\"><div class=\"n\">02<\/div><h4>Radically simple<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">One question, one page, one clear answer. No unnecessary steps.<\/p><\/div>\n        <div class=\"value-item\"><div class=\"n\">03<\/div><h4>Free, permanently<\/div><p style=\"color:var(--text-soft);font-size:.9rem;\">Supported by light, non-intrusive advertising so every tool stays free.<\/p><\/div>\n        <div class=\"value-item\"><div class=\"n\">04<\/div><h4>Privacy respected<\/div><p style=\"color:var(--text-soft);font-size:.9rem;\">Your numbers stay in your browser. We don&#8217;t store what you calculate.<\/p><\/div>\n      <\/div>\n\n      <div class=\"two-col\">\n        <div class=\"card-plain\">\n          <h4>Where our visitors come from<\/h4>\n          <table class=\"mini\">\n            <tr><td>\ud83c\uddfa\ud83c\uddf8 United States<\/td><td>38%<\/td><\/tr>\n            <tr><td>\ud83c\uddec\ud83c\udde7 United Kingdom<\/td><td>18%<\/td><\/tr>\n            <tr><td>\ud83c\udde8\ud83c\udde6 Canada<\/td><td>14%<\/td><\/tr>\n            <tr><td>\ud83c\udde6\ud83c\uddfa Australia<\/td><td>9%<\/td><\/tr>\n            <tr><td>\ud83c\uddeb\ud83c\uddf7 France<\/td><td>7%<\/td><\/tr>\n            <tr><td>Rest of world<\/td><td>14%<\/td><\/tr>\n          <\/table>\n        <\/div>\n        <div>\n          <h2>Ready to calculate?<\/h2>\n          <p style=\"color:var(--text-soft);margin-bottom:20px;\">Jump straight into any of our 31+ calculators \u2014 no account, no waiting.<\/p>\n          <a href=\"#\/listings\" class=\"btn btn-primary\">Browse all calculators<\/a>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- ============ SERVICES ============ -->\n<section class=\"page\" id=\"page-services\" data-page=\"services\">\n  <div class=\"hero\" style=\"padding:56px 0 60px;\">\n    <div class=\"wrap\">\n      <span class=\"eyebrow\">\u25cf WHAT WE OFFER<\/span>\n      <h1 style=\"max-width:680px;\">Four calculator categories. One consistent experience.<\/h1>\n      <p class=\"lead\">Every tool on MAFtrix is grouped into one of four services, each designed around how people actually search and think.<\/p>\n    <\/div>\n  <\/div>\n  <div class=\"ticks dark\"><\/div>\n\n  <div class=\"section bg-alt\">\n    <div class=\"wrap\">\n      <div class=\"value-grid\" style=\"margin-bottom:70px;\">\n        <div class=\"value-item\"><div class=\"n\" id=\"svc-icon-fin\"><\/div><h4>Financial Calculators<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">Mortgage, loan, auto loan, interest, retirement, investment, amortization, sales tax and tip \u2014 plan every dollar with confidence.<\/p><a href=\"#\/listings\" class=\"btn btn-ghost btn-sm\" style=\"margin-top:14px;\">View financial tools \u2192<\/a><\/div>\n        <div class=\"value-item\"><div class=\"n\">\u2211<\/div><h4>Math Calculators<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">Percentage, fractions, averages, standard deviation, triangles, quadratics and a full scientific calculator.<\/p><a href=\"#\/listings\" class=\"btn btn-ghost btn-sm\" style=\"margin-top:14px;\">View math tools \u2192<\/a><\/div>\n        <div class=\"value-item\"><div class=\"n\">\u2665<\/div><h4>Health &amp; Fitness<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">BMI, BMR, calories, body fat, ideal weight and pregnancy due-date \u2014 grounded in standard clinical formulas.<\/p><a href=\"#\/listings\" class=\"btn btn-ghost btn-sm\" style=\"margin-top:14px;\">View health tools \u2192<\/a><\/div>\n        <div class=\"value-item\"><div class=\"n\">\u23f1<\/div><h4>Everyday Calculators<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">Age, date difference, GPA, grade, password generator, unit conversion and discounts \u2014 the small stuff, solved fast.<\/p><a href=\"#\/listings\" class=\"btn btn-ghost btn-sm\" style=\"margin-top:14px;\">View everyday tools \u2192<\/a><\/div>\n      <\/div>\n\n      <div class=\"section-head\">\n        <span class=\"kicker\">\/\/ How it works<\/span>\n        <h2>From question to answer in three steps<\/h2>\n      <\/div>\n      <div class=\"timeline\" style=\"margin-bottom:20px;\">\n        <div class=\"step\"><h4>Search or browse<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">Use the search bar or filter by category to find the exact calculator you need.<\/p><\/div>\n        <div class=\"step\"><h4>Enter your numbers<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">Each calculator asks only for what&#8217;s relevant \u2014 no extra clutter.<\/p><\/div>\n        <div class=\"step\"><h4>Get an instant result<\/h4><p style=\"color:var(--text-soft);font-size:.9rem;\">Your answer appears in a clear readout, with supporting figures where useful.<\/p><\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"section on-dark\">\n    <div class=\"wrap two-col\">\n      <div>\n        <span class=\"kicker\">\/\/ For AdSense partners<\/span>\n        <h2>A clean, brand-safe environment<\/h2>\n        <p style=\"color:rgba(255,255,255,.65);\">MAFtrix is built around genuinely useful, high-intent tools \u2014 the kind of content people return to again and again. That means steady, relevant traffic in a clean, professional layout.<\/p>\n      <\/div>\n      <div class=\"card-plain\" style=\"background:var(--ink-2);border-color:rgba(255,255,255,.1);\">\n        <table class=\"mini\" style=\"color:#fff;\">\n          <tr style=\"border-color:rgba(255,255,255,.1);\"><td style=\"color:rgba(255,255,255,.6);\">Content type<\/td><td style=\"color:#fff;\">Utility \/ reference<\/td><\/tr>\n          <tr style=\"border-color:rgba(255,255,255,.1);\"><td style=\"color:rgba(255,255,255,.6);\">Primary intent<\/td><td style=\"color:#fff;\">High (task completion)<\/td><\/tr>\n          <tr style=\"border-color:rgba(255,255,255,.1);\"><td style=\"color:rgba(255,255,255,.6);\">Layout<\/td><td style=\"color:#fff;\">Clean, single-purpose pages<\/td><\/tr>\n        <\/table>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- ============ LISTINGS ============ -->\n<section class=\"page\" id=\"page-listings\" data-page=\"listings\">\n  <div class=\"section\" style=\"padding-top:56px;\">\n    <div class=\"wrap\">\n      <div class=\"section-head\">\n        <span class=\"kicker\">\/\/ Full directory<\/span>\n        <h1>All calculators<\/h1>\n        <p>Search, filter by category, or scroll the full list \u2014 every calculator opens on its own dedicated page.<\/p>\n      <\/div>\n      <div class=\"filterbar\">\n        <input type=\"search\" id=\"listSearch\" placeholder=\"Search calculators\u2026\" aria-label=\"Search calculators\">\n        <button class=\"chip active\" data-cat=\"all\">All<\/button>\n        <button class=\"chip\" data-cat=\"financial\">Financial<\/button>\n        <button class=\"chip\" data-cat=\"math\">Math<\/button>\n        <button class=\"chip\" data-cat=\"health\">Health &amp; Fitness<\/button>\n        <button class=\"chip\" data-cat=\"everyday\">Everyday<\/button>\n      <\/div>\n      <div class=\"grid-photo\" id=\"listingsGrid\"><\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- ============ CONTACT ============ -->\n<section class=\"page\" id=\"page-contact\" data-page=\"contact\">\n  <div class=\"section\" style=\"padding-top:56px;\">\n    <div class=\"wrap\">\n      <div class=\"section-head\">\n        <span class=\"kicker\">\/\/ Get in touch<\/span>\n        <h1>Contact MAFtrix<\/h1>\n        <p>Found a bug, want to suggest a calculator, or interested in advertising? Send us a message.<\/p>\n      <\/div>\n      <div class=\"two-col\" style=\"align-items:start;\">\n        <div class=\"card-plain\">\n          <form id=\"contactForm\" novalidate>\n            <div class=\"form-row\">\n              <div class=\"field\">\n                <label for=\"cf-name\">Full name<\/label>\n                <input type=\"text\" id=\"cf-name\" name=\"name\" required placeholder=\"Jane Doe\">\n              <\/div>\n              <div class=\"field\">\n                <label for=\"cf-email\">Email address<\/label>\n                <input type=\"email\" id=\"cf-email\" name=\"email\" required placeholder=\"jane@example.com\">\n              <\/div>\n            <\/div>\n            <div class=\"form-row\">\n              <div class=\"field\">\n                <label for=\"cf-phone\">Phone <small>(optional)<\/small><\/label>\n                <input type=\"tel\" id=\"cf-phone\" name=\"phone\" placeholder=\"+1 (555) 123-4567\">\n              <\/div>\n              <div class=\"field\">\n                <label for=\"cf-subject\">Subject<\/label>\n                <select id=\"cf-subject\" name=\"subject\">\n                  <option>General question<\/option>\n                  <option>Suggest a calculator<\/option>\n                  <option>Report an issue<\/option>\n                  <option>Advertising \/ partnership<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n            <div class=\"field\">\n              <label for=\"cf-message\">Message<\/label>\n              <textarea id=\"cf-message\" name=\"message\" required placeholder=\"How can we help?\"><\/textarea>\n            <\/div>\n            <button type=\"submit\" class=\"btn btn-primary btn-block\">Send message<\/button>\n            <div class=\"notice\" id=\"cf-notice\">Thanks \u2014 your message has been received. We&#8217;ll reply within 1\u20132 business days.<\/div>\n          <\/form>\n        <\/div>\n        <div>\n          <div class=\"contact-info-item\">\n            <div class=\"icn\" aria-hidden=\"true\">\u2709<\/div>\n            <div><h5>Email<\/h5><p>hello@maftrix.com<\/p><\/div>\n          <\/div>\n          <div class=\"contact-info-item\">\n            <div class=\"icn\" aria-hidden=\"true\">\u260e<\/div>\n            <div><h5>Phone<\/h5><p>+1 (800) 555-0134<\/p><\/div>\n          <\/div>\n          <div class=\"contact-info-item\">\n            <div class=\"icn\" aria-hidden=\"true\">\u2691<\/div>\n            <div><h5>Location<\/h5><p>New York, NY, United States<\/p><\/div>\n          <\/div>\n          <div class=\"social-row\" aria-label=\"Social media links\">\n            <a href=\"#\" aria-label=\"MAFtrix on X \/ Twitter\">\ud835\udd4f<\/a>\n            <a href=\"#\" aria-label=\"MAFtrix on Facebook\">f<\/a>\n            <a href=\"#\" aria-label=\"MAFtrix on LinkedIn\">in<\/a>\n            <a href=\"#\" aria-label=\"MAFtrix on Instagram\">\u25ce<\/a>\n          <\/div>\n        <\/div>\n      <\/div>\n      <div style=\"margin-top:44px;\">\n        <iframe class=\"map-frame\" title=\"MAFtrix office location map\" loading=\"lazy\" src=\"https:\/\/www.google.com\/maps?q=New%20York%2C%20NY&#038;output=embed\"><\/iframe>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<!-- ============ CALCULATOR DETAIL ============ -->\n<section class=\"page\" id=\"page-calculator\" data-page=\"calculator\">\n  <div class=\"section\" style=\"padding-top:40px;\">\n    <div class=\"wrap\">\n      <div class=\"breadcrumb\"><a href=\"#\/home\">Home<\/a> \/ <a href=\"#\/listings\">Listings<\/a> \/ <span id=\"calcBreadcrumb\">Calculator<\/span><\/div>\n      <div class=\"calc-head\">\n        <div class=\"icn\" id=\"calcHeadIcon\"><\/div>\n        <div>\n          <h1 id=\"calcTitle\" style=\"margin-bottom:2px;\">Calculator<\/h1>\n          <p id=\"calcDesc\" style=\"color:var(--text-soft);margin:0;\">Description<\/p>\n        <\/div>\n      <\/div>\n      <div class=\"calc-layout\">\n        <div class=\"calc-panel\" id=\"calcMount\"><\/div>\n        <div>\n          <div class=\"side-panel\">\n            <h4>Related calculators<\/h4>\n            <ul class=\"related-list\" id=\"relatedList\"><\/ul>\n          <\/div>\n          <div class=\"ad-slot\">ADVERTISEMENT<br>300 \u00d7 250<\/div>\n        <\/div>\n      <\/div>\n      <article class=\"calc-article\" id=\"calcArticle\"><\/article>\n    <\/div>\n  <\/div>\n<\/section>\n\n<\/main>\n\n<footer>\n  <div class=\"wrap foot-grid\">\n    <div>\n      <a href=\"#\/home\" class=\"logo\"><span class=\"logo-mark\">M<\/span><span class=\"logo-word\">MAF<span class=\"accent\">trix<\/span><\/span><\/a>\n      <p style=\"max-width:280px;margin-top:16px;font-size:.9rem;\">Free, fast, no-nonsense calculators for math, finance, health and everyday life.<\/p>\n      <div class=\"social-row\" aria-label=\"Social media links\">\n        <a href=\"#\" aria-label=\"MAFtrix on X \/ Twitter\">\ud835\udd4f<\/a>\n        <a href=\"#\" aria-label=\"MAFtrix on Facebook\">f<\/a>\n        <a href=\"#\" aria-label=\"MAFtrix on LinkedIn\">in<\/a>\n        <a href=\"#\" aria-label=\"MAFtrix on Instagram\">\u25ce<\/a>\n      <\/div>\n    <\/div>\n    <div>\n      <h5>Quick links<\/h5>\n      <ul>\n        <li><a href=\"#\/home\">Home<\/a><\/li>\n        <li><a href=\"#\/about\">About<\/a><\/li>\n        <li><a href=\"#\/services\">Services<\/a><\/li>\n        <li><a href=\"#\/listings\">Listings<\/a><\/li>\n        <li><a href=\"#\/contact\">Contact<\/a><\/li>\n      <\/ul>\n    <\/div>\n    <div>\n      <h5>Popular calculators<\/h5>\n      <ul id=\"footerPopular\"><\/ul>\n    <\/div>\n    <div>\n      <h5>Contact<\/h5>\n      <ul>\n        <li>hello@maftrix.com<\/li>\n        <li>+1 (800) 555-0134<\/li>\n        <li>New York, NY, USA<\/li>\n      <\/ul>\n    <\/div>\n  <\/div>\n  <div class=\"wrap\">\n    <div class=\"ticks dark\"><\/div>\n    <div class=\"foot-bottom\">\n      <span>\u00a9 <span id=\"year\"><\/span> MAFtrix. All rights reserved.<\/span>\n      <span>Formulas provided for informational purposes only \u2014 not financial, medical or legal advice.<\/span>\n    <\/div>\n  <\/div>\n<\/footer>\n\n<script>\n\/* ===================================================================\n   ICONS \u2014 small inline SVG library, reused across calculator cards\n=================================================================== *\/\nconst ICONS = {\n  home:'<path d=\"M4 12 12 5l8 7\"\/><path d=\"M6 10v9h12v-9\"\/><path d=\"M10 19v-6h4v6\"\/>',\n  car:'<path d=\"M4 16 6 9h12l2 7\"\/><rect x=\"3\" y=\"16\" width=\"18\" height=\"4\" rx=\"1\"\/><circle cx=\"7.5\" cy=\"20\" r=\"1.4\"\/><circle cx=\"16.5\" cy=\"20\" r=\"1.4\"\/>',\n  percent:'<line x1=\"5\" y1=\"19\" x2=\"19\" y2=\"5\"\/><circle cx=\"7.5\" cy=\"7.5\" r=\"2.3\"\/><circle cx=\"16.5\" cy=\"16.5\" r=\"2.3\"\/>',\n  coins:'<ellipse cx=\"9\" cy=\"8\" rx=\"6\" ry=\"3.4\"\/><path d=\"M3 8v5c0 1.9 2.7 3.4 6 3.4s6-1.5 6-3.4V8\"\/><ellipse cx=\"16\" cy=\"14.5\" rx=\"5\" ry=\"2.7\" fill=\"none\"\/>',\n  chart:'<path d=\"M4 20V10\"\/><path d=\"M11 20V4\"\/><path d=\"M18 20v-7\"\/><path d=\"M3 20h18\"\/>',\n  scale:'<path d=\"M12 3v18\"\/><path d=\"M5 7h14\"\/><path d=\"M5 7 2.5 13a2.5 2.5 0 0 0 5 0Z\"\/><path d=\"M19 7l-2.5 6a2.5 2.5 0 0 0 5 0Z\"\/>',\n  heart:'<path d=\"M12 20s-7-4.4-9.4-9A5 5 0 0 1 12 6a5 5 0 0 1 9.4 5c-2.4 4.6-9.4 9-9.4 9Z\"\/>',\n  activity:'<path d=\"M3 12h4l2 7 4-14 2 7h6\"\/>',\n  baby:'<circle cx=\"12\" cy=\"8\" r=\"4\"\/><path d=\"M6 20c0-3.3 2.7-6 6-6s6 2.7 6 6\"\/>',\n  calendar:'<rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2\"\/><path d=\"M3 10h18\"\/><path d=\"M8 3v4\"\/><path d=\"M16 3v4\"\/>',\n  clock:'<circle cx=\"12\" cy=\"12\" r=\"9\"\/><path d=\"M12 7v5l3.5 2\"\/>',\n  grad:'<path d=\"M2 9 12 4l10 5-10 5Z\"\/><path d=\"M6 11v5c2 2 10 2 12 0v-5\"\/>',\n  dice:'<rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"3\"\/><circle cx=\"9\" cy=\"9\" r=\"1.1\"\/><circle cx=\"15\" cy=\"9\" r=\"1.1\"\/><circle cx=\"9\" cy=\"15\" r=\"1.1\"\/><circle cx=\"15\" cy=\"15\" r=\"1.1\"\/><circle cx=\"12\" cy=\"12\" r=\"1.1\"\/>',\n  triangle:'<path d=\"M12 4 21 20H3Z\"\/>',\n  equation:'<path d=\"M5 6h9\"\/><path d=\"M5 12h6\"\/><path d=\"M14 12l5 7\"\/><path d=\"M19 12l-5 7\"\/>',\n  calc:'<rect x=\"5\" y=\"3\" width=\"14\" height=\"18\" rx=\"2\"\/><rect x=\"7.5\" y=\"5.5\" width=\"9\" height=\"4\" rx=\"1\"\/><circle cx=\"8.5\" cy=\"13\" r=\"1\"\/><circle cx=\"12\" cy=\"13\" r=\"1\"\/><circle cx=\"15.5\" cy=\"13\" r=\"1\"\/><circle cx=\"8.5\" cy=\"17\" r=\"1\"\/><circle cx=\"12\" cy=\"17\" r=\"1\"\/><circle cx=\"15.5\" cy=\"17\" r=\"1\"\/>',\n  lock:'<rect x=\"5\" y=\"10\" width=\"14\" height=\"10\" rx=\"2\"\/><path d=\"M8 10V7a4 4 0 0 1 8 0v3\"\/>',\n  ruler:'<rect x=\"3\" y=\"8\" width=\"18\" height=\"8\" rx=\"1.5\"\/><path d=\"M7 8v3\"\/><path d=\"M11 8v3\"\/><path d=\"M15 8v3\"\/><path d=\"M19 8v3\"\/>',\n  ticket:'<path d=\"M4 8a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 0 0 4v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2a2 2 0 0 0 0-4Z\"\/><path d=\"M14 6v12\" stroke-dasharray=\"2 3\"\/>',\n  search:'<circle cx=\"11\" cy=\"11\" r=\"7\"\/><path d=\"M21 21l-4.3-4.3\"\/>'\n};\nfunction icon(name,size=24){\n  const p = ICONS[name] || ICONS.calc;\n  return `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\">${p}<\/svg>`;\n}\n\n\/* ===================================================================\n   HELPERS\n=================================================================== *\/\nconst $ = id => document.getElementById(id);\nconst money = n => isFinite(n) ? n.toLocaleString('en-US',{style:'currency',currency:'USD',maximumFractionDigits:2}) : '\u2014';\nconst num = (v,d=2) => isFinite(v) ? Number(v).toLocaleString('en-US',{maximumFractionDigits:d,minimumFractionDigits:0}) : '\u2014';\nfunction readout(id,label,value,subPairs){\n  let sub = '';\n  if(subPairs && subPairs.length){\n    sub = '<div class=\"r-sub\">' + subPairs.map(p=>`<span>${p[0]}: <b>${p[1]}<\/b><\/span>`).join('') + '<\/div>';\n  }\n  const el = $(id);\n  el.className = 'readout';\n  el.hidden = false;\n  el.innerHTML = `<div class=\"r-label\">${label}<\/div><div class=\"r-value\">${value}<\/div>${sub}`;\n}\nfunction readoutErr(id,msg){\n  const el = $(id);\n  el.className = 'readout error';\n  el.hidden = false;\n  el.innerHTML = `<div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">${msg}<\/div>`;\n}\nconst CHART_COLORS = ['#0EA5A0','#F5A623','#16273D','#5B8DEF','#E0533D','#8B5CF6','#1E9E6B','#94A3B8'];\nconst _chartRegistry = {};\nfunction makeChart(canvasId,config){\n  const el = $(canvasId);\n  if(!el) return null;\n  if(_chartRegistry[canvasId]){ try{ _chartRegistry[canvasId].destroy(); }catch(e){} }\n  try{\n    if(typeof Chart === 'undefined'){\n      el.parentElement.innerHTML = '<p style=\"color:var(--text-soft);font-size:.85rem;padding:8px 0;\">Chart unavailable in this preview \u2014 all figures are still shown in the tables above.<\/p>';\n      return null;\n    }\n    const chart = new Chart(el.getContext('2d'), config);\n    _chartRegistry[canvasId] = chart;\n    return chart;\n  }catch(e){\n    console.error('Chart render failed for', canvasId, e);\n    return null;\n  }\n}\nfunction pieChart(canvasId,labels,data){\n  return makeChart(canvasId,{\n    type:'doughnut',\n    data:{labels, datasets:[{data, backgroundColor:CHART_COLORS, borderWidth:2, borderColor:'#fff'}]},\n    options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}}}\n  });\n}\nfunction lineChart(canvasId,labels,datasets){\n  return makeChart(canvasId,{\n    type:'line',\n    data:{labels, datasets:datasets.map((d,i)=>({...d, borderColor:CHART_COLORS[i], backgroundColor:CHART_COLORS[i]+'22', fill:d.fill!==undefined?d.fill:false, tension:.25, pointRadius:0, borderWidth:2}))},\n    options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{boxWidth:11,font:{size:11}}}},scales:{y:{ticks:{font:{size:10}}},x:{ticks:{font:{size:10},maxTicksLimit:8}}}}\n  });\n}\nfunction barChart(canvasId,labels,datasets){\n  return makeChart(canvasId,{\n    type:'bar',\n    data:{labels, datasets:datasets.map((d,i)=>({...d, backgroundColor:d.backgroundColor||CHART_COLORS[i]}))},\n    options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:datasets.length>1,position:'bottom',labels:{boxWidth:11,font:{size:11}}}},scales:{y:{ticks:{font:{size:10}}},x:{ticks:{font:{size:10}}}}}\n  });\n}\nfunction legendHTML(labels,data,colors){\n  colors = colors||CHART_COLORS;\n  return '<ul class=\"legend-list\">'+labels.map((l,i)=>`<li><span class=\"sw\" style=\"background:${colors[i]}\"><\/span>${l}: <b style=\"margin-left:auto;font-family:'IBM Plex Mono',monospace;\">${data[i]}<\/b><\/li>`).join('')+'<\/ul>';\n}\nconst MORTGAGE_COLORS = {pi:'#4C6FFF', tax:'#7CB342', ins:'#C0392B', pmi:'#8E44AD', hoa:'#F5A623', other:'#26C6DA'};\nfunction dotSpan(color){ return `<span style=\"display:inline-block;width:9px;height:9px;border-radius:50%;background:${color};margin-right:8px;vertical-align:middle;\"><\/span>`; }\nfunction polarToXY(cx,cy,r,angleDeg){\n  const rad = (angleDeg-90)*Math.PI\/180;\n  return { x: cx + r*Math.cos(rad), y: cy + r*Math.sin(rad) };\n}\nfunction pieSlicePath(cx,cy,r,startAngle,endAngle){\n  const start = polarToXY(cx,cy,r,startAngle);\n  const end = polarToXY(cx,cy,r,endAngle);\n  const largeArc = (endAngle-startAngle) > 180 ? 1 : 0;\n  return `M${cx},${cy} L${start.x.toFixed(2)},${start.y.toFixed(2)} A${r},${r} 0 ${largeArc} 1 ${end.x.toFixed(2)},${end.y.toFixed(2)} Z`;\n}\nfunction renderPieChart(containerId,labels,data,colors){\n  const el = $(containerId);\n  if(!el) return;\n  const total = data.reduce((a,b)=>a+b,0);\n  const size=220, r=size\/2, cx=r, cy=r;\n  let cursor=0, slices='', labelsSvg='';\n  data.forEach((v,i)=>{\n    const pct = total>0 ? v\/total*100 : 0;\n    const startAngle = total>0 ? cursor\/total*360 : 0;\n    const endAngle = total>0 ? (cursor+v)\/total*360 : 0;\n    slices += `<path d=\"${pieSlicePath(cx,cy,r,startAngle,endAngle)}\" fill=\"${colors[i]}\" stroke=\"#fff\" stroke-width=\"2.5\"><title>${labels[i]}: ${money(v)}<\/title><\/path>`;\n    if(pct>=4){\n      const mid = (startAngle+endAngle)\/2;\n      const pos = polarToXY(cx,cy,r*0.66,mid);\n      labelsSvg += `<text x=\"${pos.x.toFixed(1)}\" y=\"${pos.y.toFixed(1)}\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"#fff\" font-family=\"IBM Plex Mono, monospace\" font-weight=\"700\" font-size=\"14\">${Math.round(pct)}%<\/text>`;\n    }\n    cursor += v;\n  });\n  el.innerHTML = `<svg viewBox=\"0 0 ${size} ${size}\" width=\"100%\" height=\"100%\" style=\"max-width:230px;display:block;margin:0 auto;\">${slices}${labelsSvg}<\/svg>`;\n}\nfunction monthlyPayment(principal, annualRatePct, months){\n  const r = (annualRatePct\/100)\/12;\n  if(r === 0) return principal\/months;\n  return principal * (r*Math.pow(1+r,months)) \/ (Math.pow(1+r,months)-1);\n}\nfunction fv(pv, pmt, ratePct, periods){\n  const i = ratePct\/100;\n  if(i===0) return pv + pmt*periods;\n  return pv*Math.pow(1+i,periods) + pmt*((Math.pow(1+i,periods)-1)\/i);\n}\n\n\/* ===================================================================\n   CALCULATOR DATA \u2014 each item: slug, name, cat, code, icon shape, desc,\n   photo seed (listings), render(mountEl)\n=================================================================== *\/\nconst CATS = {\n  financial:{label:'Financial', code:'FIN'},\n  math:{label:'Math', code:'MATH'},\n  health:{label:'Health & Fitness', code:'HLTH'},\n  everyday:{label:'Everyday', code:'MISC'}\n};\n\nconst CALCULATORS = [];\nfunction reg(o){ CALCULATORS.push(o); }\n\n\/* ---------- FINANCIAL ---------- *\/\nreg({slug:'mortgage',name:'Mortgage Calculator',cat:'financial',icn:'home',photo:11,\n  desc:'Estimate your monthly mortgage payment with tax\/cost escalation, extra payments, biweekly payback comparison, and a full amortization schedule.',\n  render(el){\n    const now = new Date();\n    const monthNamesShort=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];\n    function monthYearSelect(idPrefix, defMonth, defYear){\n      const opts = monthNamesShort.map((mn,i)=>`<option value=\"${i+1}\" ${(i+1)===defMonth?'selected':''}>${mn}<\/option>`).join('');\n      return `<select id=\"${idPrefix}-m\">${opts}<\/select><input type=\"number\" id=\"${idPrefix}-y\" value=\"${defYear}\" style=\"max-width:82px;\">`;\n    }\n    el.innerHTML = `\n      <div class=\"mortgage-shell\">\n      <span class=\"badge-popular\">\u2605 Most popular calculator<\/span>\n      <div class=\"field\"><label>Home price ($)<\/label><input type=\"number\" id=\"m-price\" value=\"325000\"><\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Down payment<\/label>\n          <div class=\"unit-group\"><input type=\"number\" id=\"m-down\" value=\"10\"><select id=\"m-down-unit\"><option value=\"%\">%<\/option><option value=\"$\">$<\/option><\/select><\/div>\n        <\/div>\n        <div class=\"field\"><label>Loan term<\/label>\n          <select id=\"m-term\"><option value=\"30\" selected>30 years<\/option><option value=\"20\">20 years<\/option><option value=\"15\">15 years<\/option><option value=\"10\">10 years<\/option><\/select>\n        <\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Interest rate (annual %)<\/label><input type=\"number\" step=\"0.001\" id=\"m-rate\" value=\"6.85\"><\/div>\n        <div class=\"field\"><label>Start date<\/label>\n          <div style=\"display:flex;gap:8px;\">\n            <select id=\"m-start-month\">${monthNamesShort.map((mn,i)=>`<option value=\"${i+1}\" ${i===now.getMonth()?'selected':''}>${mn}<\/option>`).join('')}<\/select>\n            <input type=\"number\" id=\"m-start-year\" value=\"${now.getFullYear()}\" style=\"max-width:100px;\">\n          <\/div>\n        <\/div>\n      <\/div>\n      <label class=\"adv-toggle\"><input type=\"checkbox\" id=\"m-adv-check\" checked> Include property tax, insurance, PMI &amp; HOA<\/label>\n      <div class=\"adv-panel show\" id=\"m-adv-panel\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Property taxes (annual)<\/label>\n            <div class=\"unit-group\"><input type=\"number\" step=\"0.01\" id=\"m-tax\" value=\"1.1\"><select id=\"m-tax-unit\"><option value=\"%\">%<\/option><option value=\"$\">$<\/option><\/select><\/div>\n          <\/div>\n          <div class=\"field\"><label>Home insurance (annual $)<\/label><input type=\"number\" id=\"m-ins\" value=\"1350\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>PMI insurance (annual $)<\/label><input type=\"number\" id=\"m-pmi\" value=\"780\"><\/div>\n          <div class=\"field\"><label>HOA fee (annual $)<\/label><input type=\"number\" id=\"m-hoa\" value=\"300\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>Other costs (annual $)<\/label><input type=\"number\" id=\"m-other\" value=\"1800\"><\/div>\n        <span class=\"more-link\" id=\"m-more-link\">+ More options<\/span>\n        <div class=\"adv-panel more-panel-card\" id=\"m-more-panel\">\n          <div class=\"section-tag\">Annual tax &amp; cost increase<\/div>\n          <div class=\"form-row\">\n            <div class=\"field\"><label>Property taxes increase (%\/yr)<\/label><input type=\"number\" id=\"m-tax-inc\" value=\"0\"><\/div>\n            <div class=\"field\"><label>Home insurance increase (%\/yr)<\/label><input type=\"number\" id=\"m-ins-inc\" value=\"0\"><\/div>\n          <\/div>\n          <div class=\"form-row\">\n            <div class=\"field\"><label>HOA fee increase (%\/yr)<\/label><input type=\"number\" id=\"m-hoa-inc\" value=\"0\"><\/div>\n            <div class=\"field\"><label>Other costs increase (%\/yr)<\/label><input type=\"number\" id=\"m-other-inc\" value=\"0\"><\/div>\n          <\/div>\n          <div class=\"section-tag\">Extra payments<\/div>\n          <div class=\"field\"><label>Extra monthly pay ($)<\/label>\n            <div class=\"extra-row\"><input type=\"number\" id=\"m-ex-mo\" value=\"0\"><span>from<\/span>${monthYearSelect('m-ex-mo-start', now.getMonth()+1, now.getFullYear())}<\/div>\n          <\/div>\n          <div class=\"field\"><label>Extra yearly pay ($)<\/label>\n            <div class=\"extra-row\"><input type=\"number\" id=\"m-ex-yr\" value=\"0\"><span>from<\/span>${monthYearSelect('m-ex-yr-start', now.getMonth()+1, now.getFullYear())}<\/div>\n          <\/div>\n          <div class=\"field\"><label>Extra one-time pay ($)<\/label>\n            <div class=\"extra-row\"><input type=\"number\" id=\"m-ex-one\" value=\"0\"><span>in<\/span>${monthYearSelect('m-ex-one-date', now.getMonth()+1, now.getFullYear())}<\/div>\n          <\/div>\n          <label class=\"adv-toggle\"><input type=\"checkbox\" id=\"m-biweekly-check\"> Show biweekly payback results<\/label>\n        <\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <button class=\"btn btn-primary btn-block\" id=\"m-btn\">Calculate<\/button>\n        <button class=\"btn btn-ghost btn-block\" id=\"m-clear\" type=\"button\">Clear<\/button>\n      <\/div>\n      <div class=\"pay-callout\" id=\"m-pay-callout\" hidden><span class=\"pc-label\">Monthly pay<\/span><span class=\"pc-value\" id=\"m-pay-value\">$0<\/span><\/div>\n\n      <div class=\"calc-section section-blue\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Payment breakdown<\/h5><\/div>\n        <div id=\"m-pay-table\"><\/div>\n      <\/div>\n\n      <div class=\"calc-section section-green\" id=\"m-charts\" hidden>\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Visual breakdown<\/h5><\/div>\n        <div class=\"chart-row-centered\">\n          <div class=\"chart-box\" id=\"m-pie\"><\/div>\n          <div id=\"m-legend\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"calc-section section-amber\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('home',16)}<\/div><h5>Loan summary<\/h5><\/div>\n        <div id=\"m-kv\"><\/div>\n      <\/div>\n\n      <div id=\"m-biweekly\"><\/div>\n\n      <div class=\"calc-section section-navy\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('calendar',16)}<\/div><h5>Amortization schedule<\/h5><\/div>\n        <div class=\"seg\" id=\"m-seg\"><button class=\"active\" data-v=\"year\">Annual schedule<\/button><button data-v=\"month\">Monthly schedule<\/button><\/div>\n        <div id=\"m-schedule\"><\/div>\n      <\/div>\n\n      <div class=\"rate-box\" id=\"m-rates\" hidden>\n        <div class=\"rb-title\">Latest mortgage rates<\/div>\n        <div class=\"rate-row\" id=\"m-rates-row\"><\/div>\n        <div class=\"rb-note\">Illustrative only, based on the rate you entered and typical term spreads \u2014 actual rates vary by lender, credit profile and market conditions.<\/div>\n        <div class=\"rb-actions\">\n          <a href=\"#\/contact\" class=\"btn btn-sm\">See your local rates<\/a>\n          <a href=\"#\/contact\" class=\"btn btn-sm\">Get pre-approval<\/a>\n        <\/div>\n      <\/div>\n      <\/div>`;\n\n    $('m-adv-check').onchange = e=> $('m-adv-panel').classList.toggle('show', e.target.checked);\n    $('m-more-link').onclick = ()=> $('m-more-panel').classList.toggle('show');\n    $('m-clear').onclick = ()=>{\n      el.querySelectorAll('input[type=number]').forEach(i=>i.value='');\n      $('m-pay-callout').hidden=true; $('m-pay-table').innerHTML=''; $('m-charts').hidden=true; $('m-kv').innerHTML='';\n      $('m-schedule').innerHTML=''; $('m-biweekly').innerHTML=''; $('m-rates').hidden=true;\n    };\n\n    const monthAbbr=['Jan.','Feb.','Mar.','Apr.','May','Jun.','Jul.','Aug.','Sep.','Oct.','Nov.','Dec.'];\n    function myLabel(m,y){ return `${m}\/${String(y).slice(-2)}`; }\n    let lastResult=null;\n\n    function buildSchedule(o){\n      let bal=o.loanAmount, m=o.startMonth, y=o.startYear, i=0;\n      const rows=[];\n      const hasAnyExtra = o.extraMonthly>0||o.extraYearly>0||o.extraOneTime>0;\n      while(bal>0.5 && i<o.months+1200){\n        const yearIdx = Math.floor(i\/12);\n        const tax = (o.baseTax*Math.pow(1+o.taxInc\/100,yearIdx))\/12;\n        const ins = (o.baseIns*Math.pow(1+o.insInc\/100,yearIdx))\/12;\n        const hoa = (o.baseHOA*Math.pow(1+o.hoaInc\/100,yearIdx))\/12;\n        const other = (o.baseOther*Math.pow(1+o.otherInc\/100,yearIdx))\/12;\n        const pmiActive = o.basePMI>0 && (bal\/o.homePrice>0.8);\n        const pmi = pmiActive ? o.basePMI\/12 : 0;\n        let extra=0;\n        if(o.extraMonthly>0 && (y>o.extraMonthlyStart.y || (y===o.extraMonthlyStart.y && m>=o.extraMonthlyStart.m))) extra+=o.extraMonthly;\n        if(o.extraYearly>0 && m===o.extraYearlyStart.m && y>=o.extraYearlyStart.y) extra+=o.extraYearly;\n        if(o.extraOneTime>0 && m===o.extraOneTimeDate.m && y===o.extraOneTimeDate.y) extra+=o.extraOneTime;\n        const int = bal*(o.rate\/100\/12);\n        const princ = Math.min((o.pay+extra)-int, bal);\n        bal -= princ;\n        rows.push({m,y,int,princ,bal:Math.max(bal,0),tax,ins,hoa,other,pmi,extra});\n        i++; m++; if(m>12){m=1;y++;}\n        if(!hasAnyExtra && i>=o.months) break;\n        if(bal<=0.5) break;\n      }\n      return rows;\n    }\n\n    function buildBiweekly(loanAmount, rate, pay){\n      let bal=loanAmount, totalInterest=0, periods=0;\n      const biPay = pay\/2, periodRate = rate\/100\/26;\n      while(bal>0.5 && periods<(50*26)){\n        periods++;\n        const int = bal*periodRate;\n        const princ = Math.min(biPay-int, bal);\n        bal -= princ; totalInterest += int;\n      }\n      return {periods, totalInterest, years: periods\/26};\n    }\n\n    function renderSchedule(mode){\n      if(!lastResult) return;\n      const {perMonth} = lastResult;\n      if(mode==='year'){\n        let rows='', idx=0;\n        while(idx<perMonth.length){\n          const yearRows = perMonth.slice(idx, idx+12);\n          const yInt = yearRows.reduce((a,r)=>a+r.int,0);\n          const yPrinc = yearRows.reduce((a,r)=>a+r.princ,0);\n          const endBal = yearRows[yearRows.length-1].bal;\n          const first=yearRows[0], last=yearRows[yearRows.length-1];\n          const yearNo = Math.floor(idx\/12)+1;\n          rows += `<tr><td>${yearNo}<\/td><td>${myLabel(first.m,first.y)}-${myLabel(last.m,last.y)}<\/td><td>${money(yInt)}<\/td><td>${money(yPrinc)}<\/td><td>${money(endBal)}<\/td><\/tr>`;\n          idx+=12;\n        }\n        $('m-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Year<\/th><th>Date<\/th><th>Interest<\/th><th>Principal<\/th><th>Ending balance<\/th><\/tr><\/thead><tbody>${rows}<\/tbody><\/table><\/div>`;\n      } else {\n        const rows = perMonth.map((r,i)=>`<tr><td>${i+1}<\/td><td>${myLabel(r.m,r.y)}<\/td><td>${money(r.int)}<\/td><td>${money(r.princ)}<\/td><td>${money(r.bal)}<\/td><\/tr>`).join('');\n        $('m-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Month<\/th><th>Date<\/th><th>Interest<\/th><th>Principal<\/th><th>Ending balance<\/th><\/tr><\/thead><tbody>${rows}<\/tbody><\/table><\/div>`;\n      }\n    }\n    $('m-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{ $('m-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); renderSchedule(b.dataset.v); });\n\n    $('m-btn').onclick = ()=>{\n      const price=+$('m-price').value, rate=+$('m-rate').value, years=+$('m-term').value;\n      const downRaw=+$('m-down').value, downUnit=$('m-down-unit').value;\n      const downAmt = downUnit==='%' ? price*downRaw\/100 : downRaw;\n      const loanAmount = price-downAmt;\n      if(price<=0 || loanAmount<=0 || years<=0 || rate<0){ readoutErr('m-pay-table','Enter a valid price, down payment and term.'); return; }\n      const downPct = downAmt\/price*100;\n      const includeExtras = $('m-adv-check').checked;\n      const taxRaw = includeExtras ? +$('m-tax').value||0 : 0;\n      const taxUnit = $('m-tax-unit').value;\n      const baseTax = taxUnit==='%' ? price*taxRaw\/100 : taxRaw;\n      const baseIns = includeExtras ? (+$('m-ins').value||0) : 0;\n      const basePMI = includeExtras ? (+$('m-pmi').value||0) : 0;\n      const baseHOA = includeExtras ? (+$('m-hoa').value||0) : 0;\n      const baseOther = includeExtras ? (+$('m-other').value||0) : 0;\n      const taxInc=+$('m-tax-inc').value||0, insInc=+$('m-ins-inc').value||0, hoaInc=+$('m-hoa-inc').value||0, otherInc=+$('m-other-inc').value||0;\n      const extraMonthly=+$('m-ex-mo').value||0, extraYearly=+$('m-ex-yr').value||0, extraOneTime=+$('m-ex-one').value||0;\n      const extraMonthlyStart={m:+$('m-ex-mo-start-m').value,y:+$('m-ex-mo-start-y').value};\n      const extraYearlyStart={m:+$('m-ex-yr-start-m').value,y:+$('m-ex-yr-start-y').value};\n      const extraOneTimeDate={m:+$('m-ex-one-date-m').value,y:+$('m-ex-one-date-y').value};\n      const startMonth=+$('m-start-month').value, startYear=+$('m-start-year').value;\n      const months = years*12;\n      const pay = monthlyPayment(loanAmount, rate, months);\n      const pmiActiveNow = downPct<20 &#038;&#038; basePMI>0;\n\n      $('m-pay-callout').hidden=false;\n      $('m-pay-value').textContent = money(pay);\n\n      const perMonth = buildSchedule({loanAmount,rate,months,pay,homePrice:price,startMonth,startYear,\n        baseTax,taxInc,baseIns,insInc,baseHOA,hoaInc,baseOther,otherInc,basePMI,\n        extraMonthly,extraMonthlyStart,extraYearly,extraYearlyStart,extraOneTime,extraOneTimeDate});\n      const actualPayments = perMonth.length;\n      const totalInterest = perMonth.reduce((a,r)=>a+r.int,0);\n      const totalPandI = loanAmount+totalInterest;\n      const totalTax = perMonth.reduce((a,r)=>a+r.tax,0);\n      const totalIns = perMonth.reduce((a,r)=>a+r.ins,0);\n      const totalPMI = perMonth.reduce((a,r)=>a+r.pmi,0);\n      const totalHOA = perMonth.reduce((a,r)=>a+r.hoa,0);\n      const totalOther = perMonth.reduce((a,r)=>a+r.other,0);\n      const totalExtra = perMonth.reduce((a,r)=>a+r.extra,0);\n      const first = perMonth[0];\n      const totalOutOfPocketMonthly = pay+first.tax+first.ins+first.pmi+first.hoa+first.other;\n\n      $('m-pay-table').innerHTML = `<table class=\"pay-table\">\n        <thead><tr><th><\/th><th>Monthly<\/th><th>Total<\/th><\/tr><\/thead>\n        <tbody>\n          <tr><td>${dotSpan(MORTGAGE_COLORS.pi)}Mortgage payment<\/td><td>${money(pay)}<\/td><td>${money(totalPandI)}<\/td><\/tr>\n          ${baseTax>0?`<tr><td>${dotSpan(MORTGAGE_COLORS.tax)}Property tax<\/td><td>${money(first.tax)}<\/td><td>${money(totalTax)}<\/td><\/tr>`:''}\n          ${baseIns>0?`<tr><td>${dotSpan(MORTGAGE_COLORS.ins)}Home insurance<\/td><td>${money(first.ins)}<\/td><td>${money(totalIns)}<\/td><\/tr>`:''}\n          ${pmiActiveNow?`<tr><td>${dotSpan(MORTGAGE_COLORS.pmi)}PMI insurance<\/td><td>${money(first.pmi)}<\/td><td>${money(totalPMI)}<\/td><\/tr>`:''}\n          ${baseHOA>0?`<tr><td>${dotSpan(MORTGAGE_COLORS.hoa)}HOA fee<\/td><td>${money(first.hoa)}<\/td><td>${money(totalHOA)}<\/td><\/tr>`:''}\n          ${baseOther>0?`<tr><td>${dotSpan(MORTGAGE_COLORS.other)}Other costs<\/td><td>${money(first.other)}<\/td><td>${money(totalOther)}<\/td><\/tr>`:''}\n          ${totalExtra>0?`<tr><td>Extra payments<\/td><td>\u2014<\/td><td>${money(totalExtra)}<\/td><\/tr>`:''}\n          <tr class=\"total-row\"><td>Total out-of-pocket<\/td><td>${money(totalOutOfPocketMonthly)}<\/td><td>${money(totalPandI+totalTax+totalIns+totalPMI+totalHOA+totalOther)}<\/td><\/tr>\n        <\/tbody><\/table>`;\n\n      const lastRow = perMonth[perMonth.length-1];\n      const payoffLabel = `${monthAbbr[lastRow.m-1]} ${lastRow.y}`;\n      $('m-kv').innerHTML = `<div class=\"kv-list\">\n        <div class=\"kv-row\"><span>House price<\/span><b>${money(price)}<\/b><\/div>\n        <div class=\"kv-row\"><span>Loan amount<\/span><b>${money(loanAmount)}<\/b><\/div>\n        <div class=\"kv-row\"><span>Down payment<\/span><b>${money(downAmt)} (${num(downPct,1)}%)<\/b><\/div>\n        <div class=\"kv-row\"><span>Total of ${actualPayments} mortgage payments<\/span><b>${money(totalPandI)}<\/b><\/div>\n        <div class=\"kv-row\"><span>Total interest<\/span><b>${money(totalInterest)}<\/b><\/div>\n        <div class=\"kv-row total\"><span>Mortgage payoff date<\/span><b>${payoffLabel}<\/b><\/div>\n      <\/div>`;\n\n      if($('m-biweekly-check').checked){\n        const bw = buildBiweekly(loanAmount, rate, pay);\n        const stdYears = months\/12;\n        const yearsSaved = stdYears - bw.years;\n        const interestSaved = totalInterest - bw.totalInterest;\n        $('m-biweekly').innerHTML = `<div class=\"biweekly-box\">\n          <h5>Biweekly payback results (vs. standard monthly)<\/h5>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Biweekly payment<\/span><b>${money(pay\/2)}<\/b><\/div>\n            <div class=\"m-item\"><span>Payoff time<\/span><b>${bw.years.toFixed(1)} yrs<\/b><\/div>\n            <div class=\"m-item\"><span>Time saved<\/span><b>${yearsSaved.toFixed(1)} yrs<\/b><\/div>\n            <div class=\"m-item\"><span>Interest saved<\/span><b>${money(Math.max(interestSaved,0))}<\/b><\/div>\n          <\/div>\n        <\/div>`;\n      } else { $('m-biweekly').innerHTML=''; }\n\n      $('m-rates').hidden=false;\n      const r15 = Math.max(0.1, rate-0.829), r10 = Math.max(0.1, rate-0.934);\n      $('m-rates-row').innerHTML = `\n        <div class=\"rate-item\">30 Years:<b>${num(rate,3)}%<\/b><\/div>\n        <div class=\"rate-item\">15 Years:<b>${num(r15,3)}%<\/b><\/div>\n        <div class=\"rate-item\">10 Years:<b>${num(r10,3)}%<\/b><\/div>`;\n\n      lastResult = {perMonth};\n      renderSchedule($('m-seg').querySelector('button.active').dataset.v);\n\n      \/\/ Chart rendered last and defensively \u2014 a charting failure never blocks the schedule\/summary above\n      $('m-charts').hidden=false;\n      const pieLabels=['Principal & Interest'], pieData=[+pay.toFixed(2)], pieColors=[MORTGAGE_COLORS.pi];\n      if(baseTax>0){pieLabels.push('Property Taxes');pieData.push(+first.tax.toFixed(2));pieColors.push(MORTGAGE_COLORS.tax);}\n      if(baseIns>0){pieLabels.push('Home Insurance');pieData.push(+first.ins.toFixed(2));pieColors.push(MORTGAGE_COLORS.ins);}\n      if(pmiActiveNow){pieLabels.push('PMI');pieData.push(+first.pmi.toFixed(2));pieColors.push(MORTGAGE_COLORS.pmi);}\n      if(baseHOA>0){pieLabels.push('HOA');pieData.push(+first.hoa.toFixed(2));pieColors.push(MORTGAGE_COLORS.hoa);}\n      if(baseOther>0){pieLabels.push('Other Cost');pieData.push(+first.other.toFixed(2));pieColors.push(MORTGAGE_COLORS.other);}\n      try{\n        renderPieChart('m-pie',pieLabels,pieData,pieColors);\n        const pieSum = pieData.reduce((a,b)=>a+b,0);\n        $('m-legend').innerHTML = pieLabels.map((l,i)=>{\n          const pct = pieSum>0 ? Math.round(pieData[i]\/pieSum*100) : 0;\n          return `<div style=\"display:flex;align-items:flex-start;gap:10px;margin-bottom:13px;\">\n            <span style=\"width:13px;height:13px;border-radius:4px;background:${pieColors[i]};flex:none;margin-top:3px;\"><\/span>\n            <div>\n              <div style=\"font-size:.88rem;color:var(--text);font-weight:600;\">${l}<\/div>\n              <div style=\"font-size:.82rem;color:var(--text-soft);font-family:'IBM Plex Mono',monospace;\">${money(pieData[i])}\/mo &nbsp;\u00b7&nbsp; ${pct}%<\/div>\n            <\/div>\n          <\/div>`;\n        }).join('') + `<div style=\"margin-top:8px;padding-top:12px;border-top:1px solid var(--line);display:flex;justify-content:space-between;font-size:.88rem;\"><b style=\"color:var(--ink);\">Total monthly<\/b><b style=\"font-family:'IBM Plex Mono',monospace;color:var(--ink);\">${money(pieSum)}<\/b><\/div>`;\n      }catch(e){ console.error('Mortgage chart failed', e); }\n    };\n  }});\n\nreg({slug:'loan',name:'Loan Calculator',cat:'financial',icn:'coins',photo:12,\n  desc:'A complete loan analysis hub: monthly payment, full amortization schedule, extra-payment savings simulator, and side-by-side loan comparison.',\n  render(el){\n    function amortize(principal,annualRatePct,periods,periodsPerYear,extraMonthly,oneTimeExtra,oneTimeAtPeriod){\n      const r=(annualRatePct\/100)\/periodsPerYear;\n      const basePay=r===0?principal\/periods:principal*(r*Math.pow(1+r,periods))\/(Math.pow(1+r,periods)-1);\n      let bal=principal, totalInterest=0, rows=[], p=0;\n      while(bal>0.5 && p<periods*3){\n        p++;\n        const int=bal*r;\n        let princ=Math.min(basePay-int+extraMonthly,bal);\n        if(oneTimeAtPeriod===p) princ=Math.min(princ+oneTimeExtra,bal);\n        bal=Math.max(0,bal-princ);\n        totalInterest+=int;\n        rows.push({period:p,payment:princ+int,principal:princ,interest:int,balance:bal});\n        if(bal<=0.5) break;\n      }\n      return {basePay,totalInterest,rows,periodsUsed:p};\n    }\n    function svgLineChart(series,w=560,h=200){\n      if(!series.length) return '';\n      const max=Math.max(...series,1), pad=10;\n      const xOf=i=>pad+(i\/(series.length-1||1))*(w-2*pad);\n      const yOf=v=>h-pad-(v\/max)*(h-2*pad);\n      const d=series.map((v,i)=>`${i===0?'M':'L'}${xOf(i).toFixed(1)},${yOf(v).toFixed(1)}`).join(' ');\n      const area=`${d} L${xOf(series.length-1).toFixed(1)},${h-pad} L${xOf(0).toFixed(1)},${h-pad} Z`;\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\"><path d=\"${area}\" fill=\"rgba(14,165,160,.12)\"\/><path d=\"${d}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/><\/svg>`;\n    }\n    function svgYearlyBars(yearly){\n      const max=Math.max(...yearly.map(y=>y.interest),1);\n      return `<div class=\"stat-bar-chart\">${yearly.map(y=>`<div class=\"bar-col\"><div class=\"bar\" style=\"height:${Math.max(2,(y.interest\/max)*140)}px;\" title=\"Year ${y.year}: ${money(y.interest)}\"><\/div><\/div>`).join('')}<\/div>`;\n    }\n\n    const EXAMPLES=[10000,25000,50000,100000,250000,500000];\n\n    el.innerHTML = `\n      <div class=\"ln-shell\">\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Loan amount<\/label><input type=\"number\" id=\"ln-amt\" value=\"25000\"><\/div>\n        <div class=\"field\"><label>Interest rate (annual %)<\/label><input type=\"number\" step=\"0.01\" id=\"ln-rate\" value=\"7.5\"><\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Loan term<\/label><input type=\"number\" id=\"ln-term\" value=\"5\"><\/div>\n        <div class=\"field\"><label>Term unit<\/label><select id=\"ln-term-unit\"><option value=\"years\">Years<\/option><option value=\"months\">Months<\/option><\/select><\/div>\n      <\/div>\n      <div class=\"field\"><label>Payment frequency<\/label><select id=\"ln-freq\"><option value=\"12\">Monthly<\/option><option value=\"26\">Bi-weekly<\/option><option value=\"52\">Weekly<\/option><\/select><\/div>\n      <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n\n      <span class=\"more-link\" id=\"ln-more-link\">+ Down payment, fees &amp; extra payments<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"ln-more-panel\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Down payment<\/label><input type=\"number\" id=\"ln-down\" value=\"0\"><\/div>\n          <div class=\"field\"><label>Origination fee (%)<\/label><input type=\"number\" step=\"0.01\" id=\"ln-fee\" value=\"0\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Extra payment per period<\/label><input type=\"number\" id=\"ln-extra\" value=\"0\"><\/div>\n          <div class=\"field\"><label>One-time extra payment<\/label><input type=\"number\" id=\"ln-onetime\" value=\"0\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>One-time extra applied at period #<\/label><input type=\"number\" id=\"ln-onetime-period\" value=\"12\"><\/div>\n        <div class=\"field\"><label>Loan start date<\/label><input type=\"date\" id=\"ln-start\"><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"ln-btn\" type=\"button\">Calculate loan<\/button>\n      <div id=\"ln-results\"><\/div>\n      <\/div>`;\n\n    $('ln-more-link').onclick=()=>$('ln-more-panel').classList.toggle('show');\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('ln-amt').value=chip.dataset.v; calc(); });\n    const today=new Date(); $('ln-start').value=today.toISOString().slice(0,10);\n\n    function calc(){\n      const amt=+$('ln-amt').value, rate=+$('ln-rate').value;\n      const termRaw=+$('ln-term').value, termUnit=$('ln-term-unit').value;\n      const freq=+$('ln-freq').value;\n      const down=+$('ln-down').value||0, feePct=+$('ln-fee').value||0;\n      const extra=+$('ln-extra').value||0, oneTime=+$('ln-onetime').value||0, oneTimePeriod=+$('ln-onetime-period').value||0;\n      const startDate=$('ln-start').value?new Date($('ln-start').value):new Date();\n\n      const years = termUnit==='years'?termRaw:termRaw\/12;\n      const periods=Math.round(years*freq);\n      const principal=Math.max(0,amt-down);\n      if(principal<=0||periods<=0){ $('ln-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid loan amount and term.<\/div><\/div>'; return; }\n\n      const fee=principal*feePct\/100;\n      const result=amortize(principal,rate,periods,freq,extra,oneTime,oneTimePeriod);\n      const totalPayment=principal+result.totalInterest+fee;\n      const interestPct=(result.totalInterest\/(principal+result.totalInterest))*100;\n      const msPerPeriod=(365.25\/freq)*86400000;\n      const payoffDate=new Date(startDate.getTime()+result.periodsUsed*msPerPeriod);\n\n      const freqLabel=freq===12?'month':freq===26?'period (bi-weekly)':'period (weekly)';\n\n      $('ln-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(result.basePay+extra)}<\/div><div class=\"dd-main-sub\">Payment per ${freqLabel}<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Loan summary<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Principal<\/span><b>${money(principal)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total interest<\/span><b>${money(result.totalInterest)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total payment<\/span><b>${money(totalPayment)}<\/b><\/div>\n            <div class=\"m-item\"><span>Interest % of total<\/span><b>${num(interestPct,1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Number of payments<\/span><b>${result.periodsUsed}<\/b><\/div>\n            <div class=\"m-item\"><span>Payoff date<\/span><b>${payoffDate.toLocaleDateString('en-US',{month:'short',year:'numeric'})}<\/b><\/div>\n            ${fee>0?`<div class=\"m-item\"><span>Origination fee<\/span><b>${money(fee)}<\/b><\/div>`:''}\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Principal vs. interest<\/h5><\/div>\n          <div id=\"ln-pie\"><\/div>\n          <div id=\"ln-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Balance over time<\/h5><\/div>\n          ${svgLineChart(result.rows.filter((_,i)=>i%Math.max(1,Math.round(result.rows.length\/60))===0).map(r=>r.balance))}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Interest paid per year<\/h5><\/div>\n          ${(()=>{ const yearly=[]; let curYear=1, yearInt=0, count=0;\n            result.rows.forEach(r=>{ yearInt+=r.interest; count++; if(count>=freq){ yearly.push({year:curYear,interest:yearInt}); curYear++; yearInt=0; count=0; } });\n            if(count>0) yearly.push({year:curYear,interest:yearInt});\n            return svgYearlyBars(yearly); })()}\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Extra payment simulator<\/h5><\/div>\n          <div class=\"field\"><label>Extra per ${freqLabel}: <span id=\"ln-sim-val\">0<\/span><\/label><input type=\"range\" id=\"ln-sim-extra\" min=\"0\" max=\"${Math.round(result.basePay)}\" value=\"0\"><\/div>\n          <div id=\"ln-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Amortization schedule<\/h5><\/div>\n          <div class=\"seg\" id=\"ln-sched-seg\"><button class=\"active\" data-v=\"annual\">Annual<\/button><button data-v=\"all\">All payments<\/button><\/div>\n          <div id=\"ln-schedule\"><\/div>\n          <button class=\"btn btn-ghost btn-sm\" id=\"ln-csv\" type=\"button\" style=\"margin-top:10px;\">Download CSV<\/button>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Compare against another offer<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.85rem;\">Enter a second loan offer to compare side by side with the loan above.<\/p>\n          <div class=\"form-row\">\n            <div class=\"field\"><label>Alternate rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"ln-cmp-rate\" value=\"${(rate+0.5).toFixed(2)}\"><\/div>\n            <div class=\"field\"><label>Alternate term (years)<\/label><input type=\"number\" id=\"ln-cmp-term\" value=\"${(years+1).toFixed(0)}\"><\/div>\n          <\/div>\n          <div id=\"ln-cmp-results\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Did you know?<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">Paying just ${money(Math.round(result.basePay*0.1\/10)*10)} extra per ${freqLabel} on this loan could meaningfully reduce your total interest \u2014 try the simulator above to see your exact savings.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"ln-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"ln-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Calculations are performed in your browser and are not stored. Formulas use the standard amortization method used by lenders.<\/p>`;\n\n      renderPieChart('ln-pie',['Principal','Interest'],[+principal.toFixed(2),+result.totalInterest.toFixed(2)],['#4C6FFF','#F5A623']);\n      $('ln-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Principal <b style=\"margin-left:auto;\">${money(principal)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Interest <b style=\"margin-left:auto;\">${money(result.totalInterest)}<\/b><\/div>`;\n\n      function renderSchedule(mode){\n        if(mode==='annual'){\n          const yearly=[]; let curYear=1, yPrinc=0,yInt=0,count=0,bal=principal;\n          result.rows.forEach(r=>{ yPrinc+=r.principal; yInt+=r.interest; bal=r.balance; count++; if(count>=freq){ yearly.push({year:curYear,principal:yPrinc,interest:yInt,balance:bal}); curYear++; yPrinc=0;yInt=0;count=0; } });\n          if(count>0) yearly.push({year:curYear,principal:yPrinc,interest:yInt,balance:bal});\n          $('ln-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Year<\/th><th>Principal<\/th><th>Interest<\/th><th>Ending balance<\/th><\/tr><\/thead><tbody>${yearly.map(y=>`<tr><td>${y.year}<\/td><td>${money(y.principal)}<\/td><td>${money(y.interest)}<\/td><td>${money(y.balance)}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`;\n        } else {\n          $('ln-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>#<\/th><th>Payment<\/th><th>Principal<\/th><th>Interest<\/th><th>Balance<\/th><\/tr><\/thead><tbody>${result.rows.map(r=>`<tr><td>${r.period}<\/td><td>${money(r.payment)}<\/td><td>${money(r.principal)}<\/td><td>${money(r.interest)}<\/td><td>${money(r.balance)}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`;\n        }\n      }\n      renderSchedule('annual');\n      $('ln-sched-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{ $('ln-sched-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); renderSchedule(b.dataset.v); });\n\n      $('ln-csv').onclick=()=>{\n        const csv='Period,Payment,Principal,Interest,Balance\\n'+result.rows.map(r=>`${r.period},${r.payment.toFixed(2)},${r.principal.toFixed(2)},${r.interest.toFixed(2)},${r.balance.toFixed(2)}`).join('\\n');\n        const blob=new Blob([csv],{type:'text\/csv'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='loan-amortization.csv'; a.click(); URL.revokeObjectURL(url);\n      };\n\n      const simSlider=$('ln-sim-extra');\n      function simUpdate(){\n        const simExtra=+simSlider.value;\n        $('ln-sim-val').textContent=money(simExtra);\n        const simResult=amortize(principal,rate,periods,freq,simExtra,oneTime,oneTimePeriod);\n        const interestSaved=result.totalInterest-simResult.totalInterest;\n        const periodsSaved=result.periodsUsed-simResult.periodsUsed;\n        const newPayoff=new Date(startDate.getTime()+simResult.periodsUsed*msPerPeriod);\n        $('ln-sim-result').innerHTML = `<div class=\"m-item\"><span>Interest saved<\/span><b>${money(interestSaved)}<\/b><\/div><div class=\"m-item\"><span>Time saved<\/span><b>${(periodsSaved\/freq).toFixed(1)} years<\/b><\/div><div class=\"m-item\"><span>New payoff date<\/span><b>${newPayoff.toLocaleDateString('en-US',{month:'short',year:'numeric'})}<\/b><\/div>`;\n      }\n      simSlider.oninput=simUpdate; simUpdate();\n\n      function renderComparison(){\n        const cmpRate=+$('ln-cmp-rate').value, cmpTermYears=+$('ln-cmp-term').value;\n        const cmpPeriods=Math.round(cmpTermYears*freq);\n        if(cmpPeriods<=0){ $('ln-cmp-results').innerHTML=''; return; }\n        const cmpResult=amortize(principal,cmpRate,cmpPeriods,freq,0,0,0);\n        const cmpTotal=principal+cmpResult.totalInterest;\n        const aPay=result.basePay, bPay=cmpResult.basePay;\n        const aTotalCost=totalPayment, bTotalCost=cmpTotal+fee;\n        $('ln-cmp-results').innerHTML = `\n          <table class=\"ref-table ln-compare-table\"><thead><tr><th>Feature<\/th><th>This loan (${num(rate,2)}%, ${num(years,1)}y)<\/th><th>Alternate (${num(cmpRate,2)}%, ${num(cmpTermYears,1)}y)<\/th><\/tr><\/thead><tbody>\n            <tr><td>Payment per ${freqLabel}<\/td><td class=\"${aPay<bPay?'better':''}\">${money(aPay)}<\/td><td class=\"${bPay<aPay?'better':''}\">${money(bPay)}<\/td><\/tr>\n            <tr><td>Total interest<\/td><td class=\"${result.totalInterest<cmpResult.totalInterest?'better':''}\">${money(result.totalInterest)}<\/td><td class=\"${cmpResult.totalInterest<result.totalInterest?'better':''}\">${money(cmpResult.totalInterest)}<\/td><\/tr>\n            <tr><td>Total cost<\/td><td class=\"${aTotalCost<bTotalCost?'better':''}\">${money(aTotalCost)}<\/td><td class=\"${bTotalCost<aTotalCost?'better':''}\">${money(bTotalCost)}<\/td><\/tr>\n          <\/tbody><\/table>\n          <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;margin-top:12px;\">${aTotalCost<bTotalCost?'This loan':'The alternate offer'} costs ${money(Math.abs(aTotalCost-bTotalCost))} less in total, though ${aPay<bPay?'this loan':'the alternate offer'} has the lower per-period payment \u2014 the lowest payment and the lowest total cost aren't always the same loan.<\/div>`;\n      }\n      $('ln-cmp-rate').oninput=renderComparison; $('ln-cmp-term').oninput=renderComparison;\n      renderComparison();\n\n      const shareText=`Loan payment: ${money(result.basePay+extra)}\/period. Total interest: ${money(result.totalInterest)}. Total payment: ${money(totalPayment)}. Calculated on MAFtrix.`;\n      $('ln-copy').onclick=()=>{ const done=()=>{$('ln-copy').textContent='Copied!';setTimeout(()=>$('ln-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('ln-print').onclick=()=>window.print();\n    }\n    ['ln-amt','ln-rate','ln-term','ln-term-unit','ln-freq','ln-down','ln-fee','ln-extra','ln-onetime','ln-onetime-period'].forEach(id=>{\n      $(id).addEventListener('input',calc); $(id).addEventListener('change',calc);\n    });\n    $('ln-btn').onclick=calc;\n    calc();\n  }});\n\nreg({slug:'auto-loan',name:'Auto Loan Calculator',cat:'financial',icn:'car',photo:13,\n  desc:'A complete car financing tool: monthly payment, trade-in equity, total cost of ownership, fuel cost, depreciation, and lease-vs-buy comparison.',\n  render(el){\n    function amortizeAuto(principal,annualRatePct,months,extraMonthly){\n      const r=annualRatePct\/100\/12;\n      const basePay=r===0?principal\/months:principal*(r*Math.pow(1+r,months))\/(Math.pow(1+r,months)-1);\n      let bal=principal, totalInterest=0, rows=[], m=0;\n      while(bal>0.5 && m<months*3){\n        m++;\n        const int=bal*r;\n        const princ=Math.min(basePay-int+extraMonthly,bal);\n        bal=Math.max(0,bal-princ);\n        totalInterest+=int;\n        rows.push({period:m,payment:princ+int,principal:princ,interest:int,balance:bal});\n        if(bal<=0.5) break;\n      }\n      return {basePay,totalInterest,rows,periodsUsed:m};\n    }\n    function svgLineChart(series,w=560,h=200){\n      if(!series.length) return '';\n      const max=Math.max(...series,1), pad=10;\n      const xOf=i=>pad+(i\/(series.length-1||1))*(w-2*pad);\n      const yOf=v=>h-pad-(v\/max)*(h-2*pad);\n      const d=series.map((v,i)=>`${i===0?'M':'L'}${xOf(i).toFixed(1)},${yOf(v).toFixed(1)}`).join(' ');\n      const area=`${d} L${xOf(series.length-1).toFixed(1)},${h-pad} L${xOf(0).toFixed(1)},${h-pad} Z`;\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\"><path d=\"${area}\" fill=\"rgba(14,165,160,.12)\"\/><path d=\"${d}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/><\/svg>`;\n    }\n    const EXAMPLES=[20000,30000,40000,50000];\n\n    el.innerHTML = `\n      <div class=\"al-shell\">\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Vehicle price<\/label><input type=\"number\" id=\"al-price\" value=\"28000\"><\/div>\n        <div class=\"field\"><label>Down payment<\/label><input type=\"number\" id=\"al-down\" value=\"3000\"><\/div>\n      <\/div>\n      <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Interest rate (annual %)<\/label><input type=\"number\" step=\"0.01\" id=\"al-rate\" value=\"6.9\"><\/div>\n        <div class=\"field\"><label>Term (months)<\/label><input type=\"number\" id=\"al-term\" value=\"60\"><\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Sales tax (%)<\/label><input type=\"number\" step=\"0.01\" id=\"al-tax\" value=\"6\"><\/div>\n        <div class=\"field\"><label>Title\/registration\/doc fees<\/label><input type=\"number\" id=\"al-fees\" value=\"450\"><\/div>\n      <\/div>\n\n      <span class=\"more-link\" id=\"al-more-link\">+ Trade-in, extra payments &amp; ownership costs<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"al-more-panel\">\n        <div class=\"section-tag\">Trade-in<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Trade-in value<\/label><input type=\"number\" id=\"al-trade\" value=\"0\"><\/div>\n          <div class=\"field\"><label>Amount owed on trade-in<\/label><input type=\"number\" id=\"al-trade-owed\" value=\"0\"><\/div>\n        <\/div>\n        <div class=\"section-tag\">Extra payments<\/div>\n        <div class=\"field\"><label>Extra payment per month<\/label><input type=\"number\" id=\"al-extra\" value=\"0\"><\/div>\n        <div class=\"section-tag\">Monthly ownership costs (optional)<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Insurance\/month<\/label><input type=\"number\" id=\"al-ins\" value=\"120\"><\/div>\n          <div class=\"field\"><label>Maintenance\/month<\/label><input type=\"number\" id=\"al-maint\" value=\"60\"><\/div>\n        <\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"al-btn\" type=\"button\">Calculate payment<\/button>\n      <div id=\"al-results\"><\/div>\n      <\/div>`;\n\n    $('al-more-link').onclick=()=>$('al-more-panel').classList.toggle('show');\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('al-price').value=chip.dataset.v; calc(); });\n\n    function calc(){\n      const price=+$('al-price').value, down=+$('al-down').value;\n      const rate=+$('al-rate').value, months=+$('al-term').value;\n      const taxPct=+$('al-tax').value, fees=+$('al-fees').value||0;\n      const tradeVal=+$('al-trade').value||0, tradeOwed=+$('al-trade-owed').value||0;\n      const extra=+$('al-extra').value||0;\n      const insMo=+$('al-ins').value||0, maintMo=+$('al-maint').value||0;\n\n      const tradeEquity=tradeVal-tradeOwed;\n      const tax=price*taxPct\/100;\n      const principal=Math.max(0,price+tax+fees-down-tradeEquity);\n      if(principal<=0||months<=0){ $('al-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid vehicle price and term.<\/div><\/div>'; return; }\n\n      const result=amortizeAuto(principal,rate,months,extra);\n      const totalLoanCost=principal+result.totalInterest;\n      const totalVehicleCost=price+tax+fees+result.totalInterest;\n      const today=new Date();\n      const payoffDate=new Date(today.getFullYear(),today.getMonth()+result.periodsUsed,today.getDate());\n      const trueMonthlyCost=result.basePay+extra+insMo+maintMo;\n\n      $('al-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(result.basePay+extra)}<\/div><div class=\"dd-main-sub\">Monthly payment<\/div><\/div>\n\n        ${tradeOwed>0||tradeVal>0?`<div class=\"gpa-insight\" style=\"background:${tradeEquity>=0?'#0EA5A018':'#E0533D18'};border:1px solid ${tradeEquity>=0?'#0EA5A044':'#E0533D44'};\">Trade-in equity: <b>${money(tradeEquity)}<\/b>${tradeEquity<0?' \u2014 this negative equity is being added to your new loan amount.':' \u2014 this is reducing your amount financed.'}<\/div>`:''}\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('car',16)}<\/div><h5>Loan summary<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Amount financed<\/span><b>${money(principal)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total interest<\/span><b>${money(result.totalInterest)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total loan cost<\/span><b>${money(totalLoanCost)}<\/b><\/div>\n            <div class=\"m-item\"><span>Sales tax<\/span><b>${money(tax)}<\/b><\/div>\n            <div class=\"m-item\"><span>Fees<\/span><b>${money(fees)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total vehicle cost<\/span><b>${money(totalVehicleCost)}<\/b><\/div>\n            <div class=\"m-item\"><span>Payoff date<\/span><b>${payoffDate.toLocaleDateString('en-US',{month:'short',year:'numeric'})}<\/b><\/div>\n            <div class=\"m-item\"><span>Number of payments<\/span><b>${result.periodsUsed}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Amount financed breakdown<\/h5><\/div>\n          <div id=\"al-pie\"><\/div>\n          <div id=\"al-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Balance over time<\/h5><\/div>\n          ${svgLineChart(result.rows.filter((_,i)=>i%Math.max(1,Math.round(result.rows.length\/60))===0).map(r=>r.balance))}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>True monthly cost of ownership<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Loan payment<\/span><b>${money(result.basePay+extra)}<\/b><\/div>\n            <div class=\"m-item\"><span>Insurance<\/span><b>${money(insMo)}<\/b><\/div>\n            <div class=\"m-item\"><span>Maintenance<\/span><b>${money(maintMo)}<\/b><\/div>\n            <div class=\"m-item\"><span>True monthly cost<\/span><b>${money(trueMonthlyCost)}<\/b><\/div>\n          <\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Add fuel costs below for the complete picture.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Down payment simulator<\/h5><\/div>\n          <div class=\"field\"><label>Down payment: <span id=\"al-dp-val\">${num(down\/price*100,0)}<\/span>%<\/label><input type=\"range\" id=\"al-dp-slider\" min=\"0\" max=\"50\" value=\"${Math.min(50,Math.round(down\/price*100))}\"><\/div>\n          <div id=\"al-dp-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Extra payment simulator<\/h5><\/div>\n          <div class=\"field\"><label>Extra per month: <span id=\"al-sim-val\">$0<\/span><\/label><input type=\"range\" id=\"al-sim-extra\" min=\"0\" max=\"${Math.round(result.basePay)}\" value=\"0\"><\/div>\n          <div id=\"al-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Loan term comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Term<\/th><th>Monthly payment<\/th><th>Total interest<\/th><\/tr><\/thead><tbody>\n            ${[36,48,60,72,84].map(t=>{ const r=amortizeAuto(principal,rate,t,0); return `<tr><td>${t} months<\/td><td>${money(r.basePay)}<\/td><td>${money(r.totalInterest)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Amortization schedule<\/h5><\/div>\n          <div class=\"seg\" id=\"al-sched-seg\"><button class=\"active\" data-v=\"annual\">Annual<\/button><button data-v=\"all\">All payments<\/button><\/div>\n          <div id=\"al-schedule\"><\/div>\n          <button class=\"btn btn-ghost btn-sm\" id=\"al-csv\" type=\"button\" style=\"margin-top:10px;\">Download CSV<\/button>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Fuel cost estimator<\/h5><\/div>\n          <div class=\"form-row\">\n            <div class=\"field\"><label>Annual mileage<\/label><input type=\"number\" id=\"al-miles\" value=\"12000\"><\/div>\n            <div class=\"field\"><label>Fuel price\/gallon<\/label><input type=\"number\" step=\"0.01\" id=\"al-fuelprice\" value=\"3.50\"><\/div>\n          <\/div>\n          <div class=\"field\"><label>Vehicle MPG<\/label><input type=\"number\" id=\"al-mpg\" value=\"28\"><\/div>\n          <div id=\"al-fuel-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Depreciation estimate<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.85rem;\">Based on a commonly cited approximation: ~20% value loss in year 1, then ~15%\/year after \u2014 actual depreciation varies significantly by make, model and mileage.<\/p>\n          <div id=\"al-depreciation\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Lease vs. buy (quick comparison)<\/h5><\/div>\n          <div class=\"field\"><label>Estimated monthly lease payment<\/label><input type=\"number\" id=\"al-lease\" value=\"350\"><\/div>\n          <div id=\"al-lease-result\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"al-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"al-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Calculations are performed in your browser and are not stored. Estimates only \u2014 your actual offer may include additional lender-specific terms.<\/p>`;\n\n      renderPieChart('al-pie',['Vehicle price','Sales tax','Fees'],[+price.toFixed(2),+tax.toFixed(2),+fees.toFixed(2)],['#4C6FFF','#F5A623','#26C6DA']);\n      $('al-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Vehicle price <b style=\"margin-left:auto;\">${money(price)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Sales tax <b style=\"margin-left:auto;\">${money(tax)}<\/b><\/div><div class=\"m-item\">${dotSpan('#26C6DA')}Fees <b style=\"margin-left:auto;\">${money(fees)}<\/b><\/div>`;\n\n      function renderSchedule(mode){\n        if(mode==='annual'){\n          const yearly=[]; let curYear=1,yPrinc=0,yInt=0,count=0,bal=principal;\n          result.rows.forEach(r=>{ yPrinc+=r.principal; yInt+=r.interest; bal=r.balance; count++; if(count>=12){ yearly.push({year:curYear,principal:yPrinc,interest:yInt,balance:bal}); curYear++; yPrinc=0;yInt=0;count=0; } });\n          if(count>0) yearly.push({year:curYear,principal:yPrinc,interest:yInt,balance:bal});\n          $('al-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Year<\/th><th>Principal<\/th><th>Interest<\/th><th>Ending balance<\/th><\/tr><\/thead><tbody>${yearly.map(y=>`<tr><td>${y.year}<\/td><td>${money(y.principal)}<\/td><td>${money(y.interest)}<\/td><td>${money(y.balance)}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`;\n        } else {\n          $('al-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>#<\/th><th>Payment<\/th><th>Principal<\/th><th>Interest<\/th><th>Balance<\/th><\/tr><\/thead><tbody>${result.rows.map(r=>`<tr><td>${r.period}<\/td><td>${money(r.payment)}<\/td><td>${money(r.principal)}<\/td><td>${money(r.interest)}<\/td><td>${money(r.balance)}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`;\n        }\n      }\n      renderSchedule('annual');\n      $('al-sched-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{ $('al-sched-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); renderSchedule(b.dataset.v); });\n      $('al-csv').onclick=()=>{\n        const csv='Period,Payment,Principal,Interest,Balance\\n'+result.rows.map(r=>`${r.period},${r.payment.toFixed(2)},${r.principal.toFixed(2)},${r.interest.toFixed(2)},${r.balance.toFixed(2)}`).join('\\n');\n        const blob=new Blob([csv],{type:'text\/csv'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='auto-loan-amortization.csv'; a.click(); URL.revokeObjectURL(url);\n      };\n\n      \/\/ down payment simulator\n      const dpSlider=$('al-dp-slider');\n      function dpUpdate(){\n        const dpPct=+dpSlider.value; $('al-dp-val').textContent=dpPct;\n        const simDown=price*dpPct\/100;\n        const simPrincipal=Math.max(0,price+tax+fees-simDown-tradeEquity);\n        const simR=amortizeAuto(simPrincipal,rate,months,0);\n        $('al-dp-result').innerHTML = `<div class=\"m-item\"><span>Down payment<\/span><b>${money(simDown)}<\/b><\/div><div class=\"m-item\"><span>Loan amount<\/span><b>${money(simPrincipal)}<\/b><\/div><div class=\"m-item\"><span>Monthly payment<\/span><b>${money(simR.basePay)}<\/b><\/div><div class=\"m-item\"><span>Total interest<\/span><b>${money(simR.totalInterest)}<\/b><\/div>`;\n      }\n      dpSlider.oninput=dpUpdate; dpUpdate();\n\n      \/\/ extra payment simulator\n      const simSlider=$('al-sim-extra');\n      function simUpdate(){\n        const simExtra=+simSlider.value; $('al-sim-val').textContent=money(simExtra);\n        const simResult=amortizeAuto(principal,rate,months,simExtra);\n        const interestSaved=result.totalInterest-simResult.totalInterest;\n        const monthsSaved=result.periodsUsed-simResult.periodsUsed;\n        $('al-sim-result').innerHTML = `<div class=\"m-item\"><span>Interest saved<\/span><b>${money(interestSaved)}<\/b><\/div><div class=\"m-item\"><span>Months saved<\/span><b>${monthsSaved}<\/b><\/div>`;\n      }\n      simSlider.oninput=simUpdate; simUpdate();\n\n      \/\/ fuel cost\n      function fuelUpdate(){\n        const miles=+$('al-miles').value||0, fuelPrice=+$('al-fuelprice').value||0, mpg=+$('al-mpg').value||1;\n        const annualFuel=(miles\/mpg)*fuelPrice;\n        $('al-fuel-result').innerHTML = `<div class=\"m-item\"><span>Monthly fuel cost<\/span><b>${money(annualFuel\/12)}<\/b><\/div><div class=\"m-item\"><span>Annual fuel cost<\/span><b>${money(annualFuel)}<\/b><\/div><div class=\"m-item\"><span>5-year fuel cost<\/span><b>${money(annualFuel*5)}<\/b><\/div>`;\n      }\n      ['al-miles','al-fuelprice','al-mpg'].forEach(id=>$(id).oninput=fuelUpdate); fuelUpdate();\n\n      \/\/ depreciation\n      function depUpdate(){\n        const y1=price*0.8, y3=y1*Math.pow(0.85,2), y5=y1*Math.pow(0.85,4), y10=y1*Math.pow(0.85,9);\n        $('al-depreciation').innerHTML = `<table class=\"ref-table\"><thead><tr><th>Age<\/th><th>Estimated value<\/th><\/tr><\/thead><tbody>\n          <tr><td>New<\/td><td>${money(price)}<\/td><\/tr>\n          <tr><td>After 1 year<\/td><td>${money(y1)}<\/td><\/tr>\n          <tr><td>After 3 years<\/td><td>${money(y3)}<\/td><\/tr>\n          <tr><td>After 5 years<\/td><td>${money(y5)}<\/td><\/tr>\n          <tr><td>After 10 years<\/td><td>${money(y10)}<\/td><\/tr>\n        <\/tbody><\/table>`;\n      }\n      depUpdate();\n\n      \/\/ lease vs buy\n      function leaseUpdate(){\n        const leasePay=+$('al-lease').value||0;\n        const leaseTotalCost=leasePay*months;\n        const buyTotalCost=down+totalLoanCost;\n        $('al-lease-result').innerHTML = `<table class=\"ref-table\"><tbody>\n          <tr><td>Leasing \u2014 total cost over ${months} months<\/td><td>${money(leaseTotalCost)}<\/td><\/tr>\n          <tr><td>Buying \u2014 total cost over ${months} months<\/td><td>${money(buyTotalCost)}<\/td><\/tr>\n        <\/tbody><\/table>\n        <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;margin-top:10px;\">${leaseTotalCost<buyTotalCost?'Leasing costs less over this period, but you own no asset at the end.':'Buying costs more upfront but you own the vehicle (worth an estimated '+money(price*Math.pow(0.85,Math.max(0,months\/12-1))*0.8)+') at the end \u2014 leasing never builds ownership equity.'}<\/div>`;\n      }\n      $('al-lease').oninput=leaseUpdate; leaseUpdate();\n\n      const shareText=`Auto loan payment: ${money(result.basePay+extra)}\/month. Total interest: ${money(result.totalInterest)}. Total vehicle cost: ${money(totalVehicleCost)}. Calculated on MAFtrix.`;\n      $('al-copy').onclick=()=>{ const done=()=>{$('al-copy').textContent='Copied!';setTimeout(()=>$('al-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('al-print').onclick=()=>window.print();\n    }\n    ['al-price','al-down','al-rate','al-term','al-tax','al-fees','al-trade','al-trade-owed','al-extra','al-ins','al-maint'].forEach(id=>{\n      $(id).addEventListener('input',calc);\n    });\n    $('al-btn').onclick=calc;\n    calc();\n  }});\n\nreg({slug:'interest',name:'Simple Interest Calculator',cat:'financial',icn:'percent',photo:14,\n  desc:'A complete simple interest resource: forward and reverse calculators, day-count conventions, simple vs compound comparison, and year-by-year breakdown.',\n  render(el){\n    function svgLineChart(series,w=560,h=200){\n      if(!series.length) return '';\n      const max=Math.max(...series,1), pad=10;\n      const xOf=i=>pad+(i\/(series.length-1||1))*(w-2*pad);\n      const yOf=v=>h-pad-(v\/max)*(h-2*pad);\n      const d=series.map((v,i)=>`${i===0?'M':'L'}${xOf(i).toFixed(1)},${yOf(v).toFixed(1)}`).join(' ');\n      const area=`${d} L${xOf(series.length-1).toFixed(1)},${h-pad} L${xOf(0).toFixed(1)},${h-pad} Z`;\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\"><path d=\"${area}\" fill=\"rgba(14,165,160,.12)\"\/><path d=\"${d}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/><\/svg>`;\n    }\n    const EXAMPLES=[1000,5000,10000,50000,100000];\n    const TIME_UNITS={days:1\/365,weeks:7\/365,months:1\/12,years:1};\n\n    el.innerHTML = `\n      <div class=\"si-shell\">\n      <div class=\"seg\" id=\"si-mode-seg\">\n        <button class=\"active\" data-v=\"calc\">Calculate interest<\/button>\n        <button data-v=\"reverse\">Find P, rate or time<\/button>\n        <button data-v=\"compare\">Simple vs. compound<\/button>\n      <\/div>\n\n      <div id=\"si-calc-fields\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Principal<\/label><input type=\"number\" id=\"si-p\" value=\"5000\"><\/div>\n          <div class=\"field\"><label>Annual rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"si-r\" value=\"5\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Time<\/label><input type=\"number\" step=\"0.1\" id=\"si-t\" value=\"3\"><\/div>\n          <div class=\"field\"><label>Time unit<\/label><select id=\"si-t-unit\"><option value=\"years\" selected>Years<\/option><option value=\"months\">Months<\/option><option value=\"weeks\">Weeks<\/option><option value=\"days\">Days<\/option><\/select><\/div>\n        <\/div>\n        <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n        <div class=\"field\"><label>Day-count convention (for daily rate figures)<\/label><select id=\"si-daycount\"><option value=\"365\">365 days\/year<\/option><option value=\"360\">360 days\/year (Banker's Rule)<\/option><\/select><\/div>\n      <\/div>\n\n      <div id=\"si-reverse-fields\" hidden>\n        <div class=\"field\"><label>Solve for<\/label><select id=\"si-rev-target\"><option value=\"p\">Principal<\/option><option value=\"r\">Interest rate<\/option><option value=\"t\">Time (years)<\/option><\/select><\/div>\n        <div id=\"si-rev-inputs\"><\/div>\n      <\/div>\n\n      <div id=\"si-compare-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Principal<\/label><input type=\"number\" id=\"si-cmp-p\" value=\"5000\"><\/div>\n          <div class=\"field\"><label>Annual rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"si-cmp-r\" value=\"5\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Time (years)<\/label><input type=\"number\" id=\"si-cmp-t\" value=\"10\"><\/div>\n          <div class=\"field\"><label>Compounds per year<\/label><select id=\"si-cmp-n\"><option value=\"1\">Annually<\/option><option value=\"12\" selected>Monthly<\/option><option value=\"365\">Daily<\/option><\/select><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"si-results\"><\/div>\n      <\/div>`;\n\n    let mode='calc';\n    $('si-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('si-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('si-calc-fields').hidden=mode!=='calc';\n      $('si-reverse-fields').hidden=mode!=='reverse';\n      $('si-compare-fields').hidden=mode!=='compare';\n      calc();\n    });\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('si-p').value=chip.dataset.v; calc(); });\n    el.querySelectorAll('#si-calc-fields input,#si-calc-fields select').forEach(i=>{ i.addEventListener('input',calc); i.addEventListener('change',calc); });\n    el.querySelectorAll('#si-compare-fields input,#si-compare-fields select').forEach(i=>{ i.addEventListener('input',calc); i.addEventListener('change',calc); });\n\n    const REV_CONFIGS={\n      p:[['si-rev-i','Known interest',750],['si-rev-r','Rate (%)',5],['si-rev-t','Time (years)',3]],\n      r:[['si-rev-p','Principal',5000],['si-rev-i','Known interest',750],['si-rev-t','Time (years)',3]],\n      t:[['si-rev-p','Principal',5000],['si-rev-i','Known interest',750],['si-rev-r','Rate (%)',5]]\n    };\n    function renderRevInputs(){\n      const target=$('si-rev-target').value;\n      $('si-rev-inputs').innerHTML = REV_CONFIGS[target].map(([id,label,def])=>`<div class=\"field\"><label>${label}<\/label><input type=\"number\" step=\"0.01\" id=\"${id}\" value=\"${def}\"><\/div>`).join('');\n      REV_CONFIGS[target].forEach(([id])=>$(id).addEventListener('input',calc));\n      calc();\n    }\n    $('si-rev-target').onchange=renderRevInputs;\n\n    function calc(){\n      if(mode==='calc') calcForward();\n      else if(mode==='reverse') calcReverse();\n      else calcCompare();\n    }\n\n    function calcForward(){\n      const p=+$('si-p').value, r=+$('si-r').value, tRaw=+$('si-t').value, unit=$('si-t-unit').value;\n      const dayCount=+$('si-daycount').value;\n      const tYears=tRaw*TIME_UNITS[unit];\n      if(p<=0||tYears<=0){ $('si-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid principal and time.<\/div><\/div>'; return; }\n      const interest=p*(r\/100)*tYears;\n      const total=p+interest;\n      const dailyRate=r\/100\/dayCount;\n      const dailyInt=p*dailyRate, monthlyInt=p*(r\/100)\/12, annualInt=p*(r\/100);\n\n      $('si-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(interest)}<\/div><div class=\"dd-main-sub\">Simple interest earned<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Results dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Principal<\/span><b>${money(p)}<\/b><\/div>\n            <div class=\"m-item\"><span>Interest earned<\/span><b>${money(interest)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total amount<\/span><b>${money(total)}<\/b><\/div>\n            <div class=\"m-item\"><span>Rate<\/span><b>${r}%\/year<\/b><\/div>\n            <div class=\"m-item\"><span>Time<\/span><b>${tRaw} ${unit}<\/b><\/div>\n            <div class=\"m-item\"><span>Growth<\/span><b>${num(interest\/p*100,2)}%<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <div class=\"formula-box\"><div class=\"f-label\">Formula<\/div><div class=\"f-eq\">I = P \u00d7 r \u00d7 t<\/div><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">Step 1 \u2014 Convert rate to decimal: ${r}% = ${num(r\/100,4)}<br>Step 2 \u2014 Convert time to years: ${tRaw} ${unit} = ${num(tYears,4)} years<br>Step 3 \u2014 Multiply: ${money(p)} \u00d7 ${num(r\/100,4)} \u00d7 ${num(tYears,4)} = ${money(interest)}<br>Step 4 \u2014 Total amount: A = P + I = ${money(p)} + ${money(interest)} = ${money(total)}<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Effective interest breakdown<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Per day (${dayCount}-day)<\/span><b>${money(dailyInt)}<\/b><\/div>\n            <div class=\"m-item\"><span>Per week<\/span><b>${money(dailyInt*7)}<\/b><\/div>\n            <div class=\"m-item\"><span>Per month<\/span><b>${money(monthlyInt)}<\/b><\/div>\n            <div class=\"m-item\"><span>Per quarter<\/span><b>${money(monthlyInt*3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Per year<\/span><b>${money(annualInt)}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Principal vs. interest<\/h5><\/div>\n          <div id=\"si-pie\"><\/div>\n          <div id=\"si-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Growth over time<\/h5><\/div>\n          ${svgLineChart(Array.from({length:Math.min(30,Math.max(2,Math.ceil(tYears*4)))},(_,i)=>{ const yr=tYears*(i\/(Math.min(30,Math.max(2,Math.ceil(tYears*4)))-1)); return p+p*(r\/100)*yr; }))}\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Year-by-year breakdown<\/h5><\/div>\n          <div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Year<\/th><th>Interest earned<\/th><th>Total amount<\/th><th>Growth %<\/th><\/tr><\/thead><tbody>\n            ${Array.from({length:Math.max(1,Math.ceil(tYears))},(_,i)=>{ const yr=i+1; const cappedYr=Math.min(yr,tYears); const yrInt=p*(r\/100)*cappedYr; return `<tr><td>${yr}<\/td><td>${money(yrInt)}<\/td><td>${money(p+yrInt)}<\/td><td>${num(yrInt\/p*100,2)}%<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Compare interest rates<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Rate<\/th><th>Interest earned<\/th><th>Total amount<\/th><\/tr><\/thead><tbody>\n            ${[2,4,6,8,10].map(rr=>{ const ii=p*(rr\/100)*tYears; return `<tr><td>${rr}%<\/td><td>${money(ii)}<\/td><td>${money(p+ii)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Compare time periods<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Time<\/th><th>Interest earned<\/th><th>Total amount<\/th><\/tr><\/thead><tbody>\n            ${[1,3,5,10,15,20].map(yy=>{ const ii=p*(r\/100)*yy; return `<tr><td>${yy} ${yy===1?'year':'years'}<\/td><td>${money(ii)}<\/td><td>${money(p+ii)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"si-copy\" type=\"button\">Copy result<\/button>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Calculations are performed in your browser and are not stored.<\/p>`;\n\n      renderPieChart('si-pie',['Principal','Interest'],[+p.toFixed(2),+interest.toFixed(2)],['#4C6FFF','#F5A623']);\n      $('si-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Principal <b style=\"margin-left:auto;\">${money(p)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Interest <b style=\"margin-left:auto;\">${money(interest)}<\/b><\/div>`;\n      $('si-copy').onclick=()=>{ const text=`Simple interest: ${money(interest)} on ${money(p)} at ${r}% for ${tRaw} ${unit}. Total: ${money(total)}. Calculated on MAFtrix.`; const done=()=>{$('si-copy').textContent='Copied!';setTimeout(()=>$('si-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n    }\n\n    function calcReverse(){\n      const target=$('si-rev-target').value;\n      let resultVal, resultLabel, steps;\n      if(target==='p'){\n        const i=+$('si-rev-i').value, r=+$('si-rev-r').value, t=+$('si-rev-t').value;\n        if(r<=0||t<=0){ $('si-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Rate and time must be positive.<\/div><\/div>'; return; }\n        resultVal=i\/((r\/100)*t); resultLabel='Required principal';\n        steps=`P = I \u00f7 (r \u00d7 t) = ${money(i)} \u00f7 (${num(r\/100,4)} \u00d7 ${t}) = ${money(resultVal)}`;\n      } else if(target==='r'){\n        const p=+$('si-rev-p').value, i=+$('si-rev-i').value, t=+$('si-rev-t').value;\n        if(p<=0||t<=0){ $('si-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Principal and time must be positive.<\/div><\/div>'; return; }\n        resultVal=(i\/(p*t))*100; resultLabel='Required annual rate';\n        steps=`r = I \u00f7 (P \u00d7 t) = ${money(i)} \u00f7 (${money(p)} \u00d7 ${t}) = ${num(resultVal,4)}%`;\n      } else {\n        const p=+$('si-rev-p').value, i=+$('si-rev-i').value, r=+$('si-rev-r').value;\n        if(p<=0||r<=0){ $('si-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Principal and rate must be positive.<\/div><\/div>'; return; }\n        resultVal=i\/(p*(r\/100)); resultLabel='Required time (years)';\n        steps=`t = I \u00f7 (P \u00d7 r) = ${money(i)} \u00f7 (${money(p)} \u00d7 ${num(r\/100,4)}) = ${num(resultVal,4)} years`;\n      }\n      $('si-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${target==='r'?num(resultVal,4)+'%':target==='t'?num(resultVal,4)+' years':money(resultVal)}<\/div><div class=\"dd-main-sub\">${resultLabel}<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">${steps}<\/p>\n        <\/div>`;\n    }\n\n    function calcCompare(){\n      const p=+$('si-cmp-p').value, r=+$('si-cmp-r').value, t=+$('si-cmp-t').value, n=+$('si-cmp-n').value;\n      if(p<=0||t<=0){ $('si-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid principal and time.<\/div><\/div>'; return; }\n      const simpleInt=p*(r\/100)*t, simpleTotal=p+simpleInt;\n      const compoundTotal=p*Math.pow(1+(r\/100)\/n,n*t), compoundInt=compoundTotal-p;\n      const diff=compoundInt-simpleInt;\n      $('si-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(diff)}<\/div><div class=\"dd-main-sub\">Extra interest with compounding over ${t} years<\/div><\/div>\n        <table class=\"ref-table\"><thead><tr><th><\/th><th>Simple interest<\/th><th>Compound interest<\/th><\/tr><\/thead><tbody>\n          <tr><td>Interest earned<\/td><td>${money(simpleInt)}<\/td><td>${money(compoundInt)}<\/td><\/tr>\n          <tr><td>Total amount<\/td><td>${money(simpleTotal)}<\/td><td>${money(compoundTotal)}<\/td><\/tr>\n        <\/tbody><\/table>\n        <div class=\"calc-section section-blue\" style=\"margin-top:16px;\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Growth comparison<\/h5><\/div>\n          ${svgLineChart(Array.from({length:Math.min(30,Math.max(2,t+1))},(_,i)=>{ const yr=t*(i\/(Math.min(30,Math.max(2,t+1))-1)); return p*Math.pow(1+(r\/100)\/n,n*yr); }))}\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">Compound growth curves upward over time, while simple interest grows in a straight line \u2014 the gap between them widens the longer the money is invested.<\/p>\n        <\/div>\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">See the <a href=\"#\/calculator\/compound-interest\">Compound Interest Calculator<\/a> for a full year-by-year compound breakdown.<\/p>`;\n    }\n\n    renderRevInputs();\n    calc();\n  }});\n\nreg({slug:'compound-interest',name:'Compound Interest Calculator',cat:'financial',icn:'chart',photo:15,\n  desc:'A complete investment planning tool: compound growth with recurring contributions, inflation adjustment, goal-based required-contribution solver, Rule of 72, and rate\/frequency comparisons.',\n  render(el){\n    function svgLineChart(series,w=560,h=200){\n      if(!series.length) return '';\n      const max=Math.max(...series,1), pad=10;\n      const xOf=i=>pad+(i\/(series.length-1||1))*(w-2*pad);\n      const yOf=v=>h-pad-(v\/max)*(h-2*pad);\n      const d=series.map((v,i)=>`${i===0?'M':'L'}${xOf(i).toFixed(1)},${yOf(v).toFixed(1)}`).join(' ');\n      const area=`${d} L${xOf(series.length-1).toFixed(1)},${h-pad} L${xOf(0).toFixed(1)},${h-pad} Z`;\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\"><path d=\"${area}\" fill=\"rgba(14,165,160,.12)\"\/><path d=\"${d}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/><\/svg>`;\n    }\n    function effMonthlyRate(annualRatePct,n){\n      const r=annualRatePct\/100;\n      if(n==='cont') return Math.exp(r\/12)-1;\n      return Math.pow(1+r\/n,n\/12)-1;\n    }\n    function growProjection(p,ratePct,years,n,monthlyContrib){\n      const i=effMonthlyRate(ratePct,n);\n      const months=Math.round(years*12);\n      let bal=p, contributed=p, rows=[];\n      for(let m=1;m<=months;m++){\n        bal=bal*(1+i)+monthlyContrib;\n        contributed+=monthlyContrib;\n        if(m%12===0||m===months) rows.push({year:Math.ceil(m\/12),month:m,balance:bal,contributed,interest:bal-contributed});\n      }\n      return {finalBalance:bal,totalContributed:contributed,totalInterest:bal-contributed,rows,months,i};\n    }\n    const EXAMPLES=[1000,5000,10000,50000];\n    const FREQ_LABELS={1:'Annually',2:'Semi-annually',4:'Quarterly',12:'Monthly',52:'Weekly',365:'Daily',cont:'Continuously'};\n\n    el.innerHTML = `\n      <div class=\"ci-shell\">\n      <div class=\"seg\" id=\"ci-mode-seg\">\n        <button class=\"active\" data-v=\"grow\">Growth calculator<\/button>\n        <button data-v=\"goal\">Goal \/ required contribution<\/button>\n        <button data-v=\"rule72\">Rule of 72<\/button>\n      <\/div>\n\n      <div id=\"ci-grow-fields\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Initial investment<\/label><input type=\"number\" id=\"ci-p\" value=\"10000\"><\/div>\n          <div class=\"field\"><label>Annual rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"ci-r\" value=\"6\"><\/div>\n        <\/div>\n        <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Duration (years)<\/label><input type=\"number\" id=\"ci-t\" value=\"10\"><\/div>\n          <div class=\"field\"><label>Compounding frequency<\/label>\n            <select id=\"ci-n\"><option value=\"1\">Annually<\/option><option value=\"2\">Semi-annually<\/option><option value=\"4\">Quarterly<\/option><option value=\"12\" selected>Monthly<\/option><option value=\"365\">Daily<\/option><option value=\"cont\">Continuously<\/option><\/select>\n          <\/div>\n        <\/div>\n        <div class=\"field\"><label>Monthly contribution <small>(optional)<\/small><\/label><input type=\"number\" id=\"ci-add\" value=\"200\"><\/div>\n        <span class=\"more-link\" id=\"ci-more-link\">+ Inflation adjustment<\/span>\n        <div class=\"adv-panel more-panel-card\" id=\"ci-more-panel\">\n          <div class=\"field\"><label>Expected annual inflation (%)<\/label><input type=\"number\" step=\"0.01\" id=\"ci-inflation\" value=\"3\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"ci-goal-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Goal amount<\/label><input type=\"number\" id=\"ci-goal-amt\" value=\"1000000\"><\/div>\n          <div class=\"field\"><label>Current savings<\/label><input type=\"number\" id=\"ci-goal-p\" value=\"10000\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Annual rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"ci-goal-r\" value=\"7\"><\/div>\n          <div class=\"field\"><label>Years to goal<\/label><input type=\"number\" id=\"ci-goal-t\" value=\"30\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"ci-rule72-fields\" hidden>\n        <div class=\"field\"><label>Solve for<\/label><select id=\"ci-r72-mode\"><option value=\"years\">Years to double (from rate)<\/option><option value=\"rate\">Required rate (from years)<\/option><\/select><\/div>\n        <div id=\"ci-r72-input\"><\/div>\n      <\/div>\n\n      <div id=\"ci-results\"><\/div>\n      <\/div>`;\n\n    let mode='grow';\n    $('ci-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('ci-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('ci-grow-fields').hidden=mode!=='grow';\n      $('ci-goal-fields').hidden=mode!=='goal';\n      $('ci-rule72-fields').hidden=mode!=='rule72';\n      calc();\n    });\n    $('ci-more-link').onclick=()=>$('ci-more-panel').classList.toggle('show');\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('ci-p').value=chip.dataset.v; calc(); });\n    el.querySelectorAll('#ci-grow-fields input,#ci-grow-fields select').forEach(i=>{ i.addEventListener('input',calc); i.addEventListener('change',calc); });\n    el.querySelectorAll('#ci-goal-fields input').forEach(i=>i.addEventListener('input',calc));\n\n    function renderR72(){\n      const rmode=$('ci-r72-mode').value;\n      $('ci-r72-input').innerHTML = rmode==='years' ? `<div class=\"field\"><label>Annual rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"ci-r72-val\" value=\"7\"><\/div>` : `<div class=\"field\"><label>Years to double<\/label><input type=\"number\" id=\"ci-r72-val\" value=\"10\"><\/div>`;\n      $('ci-r72-val').addEventListener('input',calc);\n      calc();\n    }\n    $('ci-r72-mode').onchange=renderR72;\n\n    function calc(){\n      if(mode==='grow') calcGrow();\n      else if(mode==='goal') calcGoal();\n      else calcRule72();\n    }\n\n    function calcGrow(){\n      const p=+$('ci-p').value, r=+$('ci-r').value, t=+$('ci-t').value, n=$('ci-n').value==='cont'?'cont':+$('ci-n').value, add=+$('ci-add').value||0;\n      const inflation=+$('ci-inflation').value||0;\n      if(p<0||t<=0){ $('ci-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid principal and duration.<\/div><\/div>'; return; }\n      const proj=growProjection(p,r,t,n,add);\n      const growthPct=(proj.finalBalance-p)\/p*100;\n      const effAnnualYield=(Math.pow(1+proj.i,12)-1)*100;\n      const realValue=proj.finalBalance\/Math.pow(1+inflation\/100,t);\n\n      $('ci-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(proj.finalBalance)}<\/div><div class=\"dd-main-sub\">Future value after ${t} years<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Results dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Total contributions<\/span><b>${money(proj.totalContributed)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total interest earned<\/span><b>${money(proj.totalInterest)}<\/b><\/div>\n            <div class=\"m-item\"><span>Investment growth<\/span><b>${num(growthPct,1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Effective annual yield<\/span><b>${num(effAnnualYield,2)}%<\/b><\/div>\n            ${inflation>0?`<div class=\"m-item\"><span>Inflation-adjusted value<\/span><b>${money(realValue)}<\/b><\/div>`:''}\n          <\/div>\n        <\/div>\n\n        <div class=\"formula-box\"><div class=\"f-label\">Compound interest formula<\/div><div class=\"f-eq\">A = P \u00d7 (1 + r\/n)\u207f\u1d57<\/div><\/div>\n        <p style=\"color:var(--text-soft);font-size:.85rem;\">A = final amount, P = principal, r = annual rate (decimal), n = compounding periods per year, t = time in years. Recurring contributions are applied monthly at the effective monthly rate implied by your chosen compounding frequency.<\/p>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Principal, contributions &amp; interest<\/h5><\/div>\n          <div id=\"ci-pie\"><\/div>\n          <div id=\"ci-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Growth over time<\/h5><\/div>\n          ${svgLineChart(proj.rows.map(r=>r.balance))}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Year-by-year breakdown<\/h5><\/div>\n          <div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Year<\/th><th>Balance<\/th><th>Contributed<\/th><th>Interest earned<\/th><\/tr><\/thead><tbody>\n            ${proj.rows.map(r=>`<tr><td>${r.year}<\/td><td>${money(r.balance)}<\/td><td>${money(r.contributed)}<\/td><td>${money(r.interest)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Contribution simulator<\/h5><\/div>\n          <div class=\"field\"><label>Monthly contribution: <span id=\"ci-sim-val\">${money(add)}<\/span><\/label><input type=\"range\" id=\"ci-sim-add\" min=\"0\" max=\"${Math.max(1000,add*3)}\" value=\"${add}\" step=\"10\"><\/div>\n          <div id=\"ci-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Compare interest rates<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Rate<\/th><th>Future value<\/th><\/tr><\/thead><tbody>\n            ${[3,5,7,10,12].map(rr=>{ const pr=growProjection(p,rr,t,n,add); return `<tr><td>${rr}%<\/td><td>${money(pr.finalBalance)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Compare compounding frequencies<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Frequency<\/th><th>Future value (no contributions)<\/th><\/tr><\/thead><tbody>\n            ${[1,4,12,365,'cont'].map(nn=>{ const pr=growProjection(p,r,t,nn,0); return `<tr><td>${FREQ_LABELS[nn]}<\/td><td>${money(pr.finalBalance)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">More frequent compounding increases returns, though the difference shrinks as frequency increases \u2014 daily vs. continuous compounding is nearly identical.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Simple vs. compound interest<\/h5><\/div>\n          ${(()=>{ const simpleInterest=p*(r\/100)*t; const simpleTotal=p+simpleInterest; const noContribCompound=growProjection(p,r,t,n,0);\n            return `<table class=\"ref-table\"><tbody><tr><td>Simple interest total<\/td><td>${money(simpleTotal)}<\/td><\/tr><tr><td>Compound interest total (no contributions)<\/td><td>${money(noContribCompound.finalBalance)}<\/td><\/tr><tr><td>Extra from compounding<\/td><td>${money(noContribCompound.finalBalance-simpleTotal)}<\/td><\/tr><\/tbody><\/table>`; })()}\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">See the <a href=\"#\/calculator\/interest\">Simple Interest Calculator<\/a> for a dedicated comparison tool.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"ci-copy\" type=\"button\">Copy result<\/button>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Calculations are performed in your browser and are not stored. Estimates only \u2014 actual investment returns are never guaranteed.<\/p>`;\n\n      renderPieChart('ci-pie',['Principal','Contributions','Interest'],[+p.toFixed(2),+(proj.totalContributed-p).toFixed(2),+proj.totalInterest.toFixed(2)],['#4C6FFF','#26C6DA','#F5A623']);\n      $('ci-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Principal <b style=\"margin-left:auto;\">${money(p)}<\/b><\/div><div class=\"m-item\">${dotSpan('#26C6DA')}Contributions <b style=\"margin-left:auto;\">${money(proj.totalContributed-p)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Interest <b style=\"margin-left:auto;\">${money(proj.totalInterest)}<\/b><\/div>`;\n\n      const simSlider=$('ci-sim-add');\n      function simUpdate(){\n        const simAdd=+simSlider.value; $('ci-sim-val').textContent=money(simAdd);\n        const simProj=growProjection(p,r,t,n,simAdd);\n        $('ci-sim-result').innerHTML = `<div class=\"m-item\"><span>Future value<\/span><b>${money(simProj.finalBalance)}<\/b><\/div><div class=\"m-item\"><span>vs. current setting<\/span><b>${simProj.finalBalance>=proj.finalBalance?'+':''}${money(simProj.finalBalance-proj.finalBalance)}<\/b><\/div>`;\n      }\n      simSlider.oninput=simUpdate; simUpdate();\n\n      $('ci-copy').onclick=()=>{ const text=`Future value: ${money(proj.finalBalance)} after ${t} years (${money(p)} initial + ${money(add)}\/mo). Total interest: ${money(proj.totalInterest)}. Calculated on MAFtrix.`; const done=()=>{$('ci-copy').textContent='Copied!';setTimeout(()=>$('ci-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n    }\n\n    function calcGoal(){\n      const goal=+$('ci-goal-amt').value, p=+$('ci-goal-p').value, r=+$('ci-goal-r').value, t=+$('ci-goal-t').value;\n      if(goal<=0||t<=0){ $('ci-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid goal amount and timeframe.<\/div><\/div>'; return; }\n      const i=effMonthlyRate(r,12);\n      const months=Math.round(t*12);\n      const futureValueOfP=p*Math.pow(1+i,months);\n      const remaining=goal-futureValueOfP;\n      let requiredMonthly;\n      if(remaining<=0){ requiredMonthly=0; }\n      else { requiredMonthly = i===0 ? remaining\/months : remaining\/(((Math.pow(1+i,months)-1))\/i); }\n\n      $('ci-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(Math.max(0,requiredMonthly))}<\/div><div class=\"dd-main-sub\">Required monthly contribution to reach ${money(goal)}<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Details<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Goal<\/span><b>${money(goal)}<\/b><\/div>\n            <div class=\"m-item\"><span>Current savings grows to<\/span><b>${money(futureValueOfP)}<\/b><\/div>\n            <div class=\"m-item\"><span>Still needed from contributions<\/span><b>${money(Math.max(0,remaining))}<\/b><\/div>\n            <div class=\"m-item\"><span>Time<\/span><b>${t} years (${months} months)<\/b><\/div>\n          <\/div>\n          ${remaining<=0?'<p style=\"color:var(--text-soft);font-size:.85rem;margin-top:10px;\">Your current savings alone are projected to reach your goal at this rate \u2014 no additional contribution required.<\/p>':''}\n        <\/div>`;\n    }\n\n    function calcRule72(){\n      const rmode=$('ci-r72-mode').value;\n      const val=+$('ci-r72-val').value;\n      if(val<=0){ $('ci-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Enter a positive value.<\/div><\/div>'; return; }\n      if(rmode==='years'){\n        const years=72\/val;\n        $('ci-results').innerHTML = `\n          <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(years,1)} years<\/div><div class=\"dd-main-sub\">Approximate time to double your money at ${val}% annual return<\/div><\/div>\n          <div class=\"formula-box\"><div class=\"f-label\">Rule of 72<\/div><div class=\"f-eq\">Years to double \u2248 72 \u00f7 Annual rate<\/div><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">The Rule of 72 is a quick mental-math approximation, not an exact calculation \u2014 it's most accurate for rates roughly between 6% and 10%.<\/p>`;\n      } else {\n        const rate=72\/val;\n        $('ci-results').innerHTML = `\n          <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(rate,2)}%<\/div><div class=\"dd-main-sub\">Approximate annual rate needed to double your money in ${val} years<\/div><\/div>\n          <div class=\"formula-box\"><div class=\"f-label\">Rule of 72<\/div><div class=\"f-eq\">Required rate \u2248 72 \u00f7 Years<\/div><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">The Rule of 72 is a quick mental-math approximation, not an exact calculation \u2014 it's most accurate for rates roughly between 6% and 10%.<\/p>`;\n      }\n    }\n\n    renderR72();\n    calc();\n  }});\n\nreg({slug:'retirement',name:'Retirement Calculator',cat:'financial',icn:'coins',photo:16,\n  desc:'A complete retirement planning dashboard: savings projection with a readiness score, withdrawal\/drawdown simulation, FIRE calculator, and retirement lifestyle expense planner.',\n  render(el){\n    function svgLineChart(series,w=560,h=200){\n      if(!series.length) return '';\n      const max=Math.max(...series,1), pad=10;\n      const xOf=i=>pad+(i\/(series.length-1||1))*(w-2*pad);\n      const yOf=v=>h-pad-(v\/max)*(h-2*pad);\n      const d=series.map((v,i)=>`${i===0?'M':'L'}${xOf(i).toFixed(1)},${yOf(v).toFixed(1)}`).join(' ');\n      const area=`${d} L${xOf(series.length-1).toFixed(1)},${h-pad} L${xOf(0).toFixed(1)},${h-pad} Z`;\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\"><path d=\"${area}\" fill=\"rgba(14,165,160,.12)\"\/><path d=\"${d}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/><\/svg>`;\n    }\n    function accumulate(currentAge,retireAge,currentSavings,monthlyContrib,returnPct){\n      const monthlyRate=Math.pow(1+returnPct\/100,1\/12)-1;\n      const months=Math.round((retireAge-currentAge)*12);\n      let bal=currentSavings, contributed=currentSavings, rows=[];\n      for(let m=1;m<=months;m++){\n        bal=bal*(1+monthlyRate)+monthlyContrib;\n        contributed+=monthlyContrib;\n        if(m%12===0||m===months) rows.push({age:currentAge+Math.ceil(m\/12),balance:bal,contributed,growth:bal-contributed});\n      }\n      return {finalBalance:bal,totalContributed:contributed,totalGrowth:bal-contributed,rows};\n    }\n    function drawdown(balanceAtRetirement,retireAge,lifeExpectancy,desiredAnnualIncome,returnPct,inflationPct,yearsToRetirement){\n      let bal=balanceAtRetirement, year=0;\n      const maxYears=Math.max(1,lifeExpectancy-retireAge);\n      let withdrawal=desiredAnnualIncome*Math.pow(1+inflationPct\/100,yearsToRetirement);\n      const rows=[];\n      while(bal>0 && year<maxYears){\n        year++;\n        bal=bal*(1+returnPct\/100)-withdrawal;\n        rows.push({age:retireAge+year,withdrawal,balance:Math.max(0,bal)});\n        withdrawal*=(1+inflationPct\/100);\n        if(bal<=0) break;\n      }\n      return {yearsLasted:year,depleted:bal<=0,rows,lastsFullRetirement:bal>0||year>=maxYears};\n    }\n\n    el.innerHTML = `\n      <div class=\"rt-shell\">\n      <div class=\"seg\" id=\"rt-mode-seg\">\n        <button class=\"active\" data-v=\"project\">Savings projection<\/button>\n        <button data-v=\"fire\">FIRE calculator<\/button>\n        <button data-v=\"lifestyle\">Lifestyle expenses<\/button>\n      <\/div>\n\n      <div id=\"rt-project-fields\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Current age<\/label><input type=\"number\" id=\"rt-age\" value=\"30\"><\/div>\n          <div class=\"field\"><label>Retirement age<\/label><input type=\"number\" id=\"rt-ret\" value=\"65\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Life expectancy<\/label><input type=\"number\" id=\"rt-life\" value=\"90\"><\/div>\n          <div class=\"field\"><label>Current savings<\/label><input type=\"number\" id=\"rt-cur\" value=\"20000\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Monthly contribution<\/label><input type=\"number\" id=\"rt-mo\" value=\"400\"><\/div>\n          <div class=\"field\"><label>Expected annual return (%)<\/label><input type=\"number\" step=\"0.1\" id=\"rt-r\" value=\"7\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Expected annual inflation (%)<\/label><input type=\"number\" step=\"0.1\" id=\"rt-infl\" value=\"2.5\"><\/div>\n          <div class=\"field\"><label>Desired annual retirement income <small>(today's dollars)<\/small><\/label><input type=\"number\" id=\"rt-income\" value=\"50000\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"rt-fire-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Current annual expenses<\/label><input type=\"number\" id=\"rt-fire-exp\" value=\"40000\"><\/div>\n          <div class=\"field\"><label>Current savings<\/label><input type=\"number\" id=\"rt-fire-cur\" value=\"20000\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Monthly savings<\/label><input type=\"number\" id=\"rt-fire-mo\" value=\"1500\"><\/div>\n          <div class=\"field\"><label>Expected annual return (%)<\/label><input type=\"number\" step=\"0.1\" id=\"rt-fire-r\" value=\"7\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>Traditional retirement age (for Coast FIRE)<\/label><input type=\"number\" id=\"rt-fire-coastage\" value=\"65\"><\/div>\n        <div class=\"field\"><label>Current age<\/label><input type=\"number\" id=\"rt-fire-age\" value=\"30\"><\/div>\n      <\/div>\n\n      <div id=\"rt-lifestyle-fields\" hidden>\n        <p style=\"color:var(--text-soft);font-size:.85rem;\">Estimate your expected monthly retirement expenses by category.<\/p>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Housing<\/label><input type=\"number\" id=\"rt-ls-housing\" value=\"1200\"><\/div>\n          <div class=\"field\"><label>Food<\/label><input type=\"number\" id=\"rt-ls-food\" value=\"500\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Healthcare<\/label><input type=\"number\" id=\"rt-ls-health\" value=\"450\"><\/div>\n          <div class=\"field\"><label>Transportation<\/label><input type=\"number\" id=\"rt-ls-transport\" value=\"300\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Travel &amp; entertainment<\/label><input type=\"number\" id=\"rt-ls-travel\" value=\"300\"><\/div>\n          <div class=\"field\"><label>Insurance<\/label><input type=\"number\" id=\"rt-ls-insurance\" value=\"200\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>Miscellaneous<\/label><input type=\"number\" id=\"rt-ls-misc\" value=\"250\"><\/div>\n      <\/div>\n\n      <div id=\"rt-results\"><\/div>\n      <\/div>`;\n\n    let mode='project';\n    $('rt-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('rt-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('rt-project-fields').hidden=mode!=='project';\n      $('rt-fire-fields').hidden=mode!=='fire';\n      $('rt-lifestyle-fields').hidden=mode!=='lifestyle';\n      calc();\n    });\n    el.querySelectorAll('#rt-project-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#rt-fire-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#rt-lifestyle-fields input').forEach(i=>i.addEventListener('input',calc));\n\n    function calc(){\n      if(mode==='project') calcProject();\n      else if(mode==='fire') calcFire();\n      else calcLifestyle();\n    }\n\n    function calcProject(){\n      const age=+$('rt-age').value, ret=+$('rt-ret').value, life=+$('rt-life').value;\n      const cur=+$('rt-cur').value, mo=+$('rt-mo').value, r=+$('rt-r').value, infl=+$('rt-infl').value||0;\n      const desiredIncome=+$('rt-income').value||0;\n      const years=ret-age;\n      if(years<=0){ $('rt-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Retirement age must be after current age.<\/div><\/div>'; return; }\n\n      const acc=accumulate(age,ret,cur,mo,r);\n      const realValue=acc.finalBalance\/Math.pow(1+infl\/100,years);\n      const dd=drawdown(acc.finalBalance,ret,life,desiredIncome,r,infl,years);\n\n      \/\/ readiness score: 25x rule (4% withdrawal) target, in future dollars at retirement\n      const futureAnnualIncome=desiredIncome*Math.pow(1+infl\/100,years);\n      const target=futureAnnualIncome*25;\n      const score=Math.min(100,Math.round(acc.finalBalance\/target*100));\n      const scoreColor=score>=90?'#1E9E6B':score>=50?'#F5A623':'#E0533D';\n      const scoreLabel=score>=90?'Ready':score>=50?'On track':'Needs improvement';\n\n      $('rt-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(acc.finalBalance)}<\/div><div class=\"dd-main-sub\">Projected balance at age ${ret}<\/div><\/div>\n\n        ${desiredIncome>0?`<div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Retirement readiness score<\/h5><\/div>\n          <div class=\"rt-gauge-wrap\">\n            <div class=\"gpa-progress\" style=\"height:16px;\"><div class=\"gpa-progress-fill\" style=\"width:${score}%;background:${scoreColor};\"><\/div><\/div>\n            <div class=\"rt-gauge-label\" style=\"background:${scoreColor}22;color:${scoreColor};\">${score}% \u2014 ${scoreLabel}<\/div>\n          <\/div>\n          <p style=\"color:var(--text-soft);font-size:.85rem;text-align:center;margin-top:6px;\">Based on a common \"25\u00d7 annual income\" target (supporting roughly a 4% withdrawal rate) \u2014 a general guideline, not personalized financial advice.<\/p>\n        <\/div>`:''}\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Results dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Total contributed<\/span><b>${money(acc.totalContributed)}<\/b><\/div>\n            <div class=\"m-item\"><span>Investment growth<\/span><b>${money(acc.totalGrowth)}<\/b><\/div>\n            <div class=\"m-item\"><span>Inflation-adjusted value<\/span><b>${money(realValue)}<\/b><\/div>\n            <div class=\"m-item\"><span>Years to retirement<\/span><b>${years}<\/b><\/div>\n            ${desiredIncome>0?`<div class=\"m-item\"><span>Years savings will last<\/span><b>${dd.lastsFullRetirement?`${life-ret}+ (to age ${life})`:dd.yearsLasted}<\/b><\/div>`:''}\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Contributions vs. growth<\/h5><\/div>\n          <div id=\"rt-pie\"><\/div>\n          <div id=\"rt-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Balance over time<\/h5><\/div>\n          ${svgLineChart(acc.rows.map(r=>r.balance))}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Accumulation timeline<\/h5><\/div>\n          <div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Age<\/th><th>Balance<\/th><th>Contributed<\/th><th>Growth<\/th><\/tr><\/thead><tbody>\n            ${acc.rows.map(rr=>`<tr><td>${rr.age}<\/td><td>${money(rr.balance)}<\/td><td>${money(rr.contributed)}<\/td><td>${money(rr.growth)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table><\/div>\n        <\/div>\n\n        ${desiredIncome>0?`<div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Withdrawal &amp; drawdown timeline<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.85rem;\">Withdrawing ${money(futureAnnualIncome)}\/year (today's ${money(desiredIncome)} adjusted for inflation to age ${ret}), growing with inflation each year after.<\/p>\n          <div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Age<\/th><th>Withdrawal<\/th><th>Remaining balance<\/th><\/tr><\/thead><tbody>\n            ${dd.rows.filter((_,i)=>i%Math.max(1,Math.round(dd.rows.length\/20))===0||i===dd.rows.length-1).map(rr=>`<tr><td>${rr.age}<\/td><td>${money(rr.withdrawal)}<\/td><td>${money(rr.balance)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table><\/div>\n        <\/div>` : ''}\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Retirement age simulator<\/h5><\/div>\n          <div class=\"field\"><label>Retire at: <span id=\"rt-age-sim-val\">${ret}<\/span><\/label><input type=\"range\" id=\"rt-age-sim\" min=\"${age+1}\" max=\"75\" value=\"${ret}\"><\/div>\n          <div id=\"rt-age-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Monthly contribution simulator<\/h5><\/div>\n          <div class=\"field\"><label>Monthly contribution: <span id=\"rt-mo-sim-val\">${money(mo)}<\/span><\/label><input type=\"range\" id=\"rt-mo-sim\" min=\"0\" max=\"${Math.max(2000,mo*3)}\" value=\"${mo}\" step=\"25\"><\/div>\n          <div id=\"rt-mo-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Compare investment returns<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Return<\/th><th>Balance at retirement<\/th><\/tr><\/thead><tbody>\n            ${[4,6,8,10,12].map(rr=>{ const a=accumulate(age,ret,cur,mo,rr); return `<tr><td>${rr}%<\/td><td>${money(a.finalBalance)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Withdrawal strategy comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Withdrawal rate<\/th><th>Annual income<\/th><th>Monthly income<\/th><\/tr><\/thead><tbody>\n            ${[3,4,5].map(wr=>{ const ann=acc.finalBalance*wr\/100; return `<tr><td>${wr}% rule<\/td><td>${money(ann)}<\/td><td>${money(ann\/12)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">The 4% rule is a commonly cited starting point for sustainable withdrawal rates, though it's a general guideline, not a guarantee \u2014 actual sustainable rates depend on market performance, retirement length, and individual circumstances.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"rt-copy\" type=\"button\">Copy result<\/button>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Calculations are performed in your browser and are not stored. Projections are estimates based on constant assumed rates \u2014 not a guarantee of future performance.<\/p>`;\n\n      renderPieChart('rt-pie',['Contributions','Growth'],[+acc.totalContributed.toFixed(2),+acc.totalGrowth.toFixed(2)],['#4C6FFF','#F5A623']);\n      $('rt-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Contributions <b style=\"margin-left:auto;\">${money(acc.totalContributed)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Growth <b style=\"margin-left:auto;\">${money(acc.totalGrowth)}<\/b><\/div>`;\n\n      const ageSimSlider=$('rt-age-sim');\n      function ageSimUpdate(){\n        const simRet=+ageSimSlider.value; $('rt-age-sim-val').textContent=simRet;\n        const simAcc=accumulate(age,simRet,cur,mo,r);\n        $('rt-age-sim-result').innerHTML = `<div class=\"m-item\"><span>Balance at retirement<\/span><b>${money(simAcc.finalBalance)}<\/b><\/div><div class=\"m-item\"><span>vs. current setting<\/span><b>${simAcc.finalBalance>=acc.finalBalance?'+':''}${money(simAcc.finalBalance-acc.finalBalance)}<\/b><\/div>`;\n      }\n      ageSimSlider.oninput=ageSimUpdate; ageSimUpdate();\n\n      const moSimSlider=$('rt-mo-sim');\n      function moSimUpdate(){\n        const simMo=+moSimSlider.value; $('rt-mo-sim-val').textContent=money(simMo);\n        const simAcc=accumulate(age,ret,cur,simMo,r);\n        $('rt-mo-sim-result').innerHTML = `<div class=\"m-item\"><span>Balance at retirement<\/span><b>${money(simAcc.finalBalance)}<\/b><\/div><div class=\"m-item\"><span>vs. current setting<\/span><b>${simAcc.finalBalance>=acc.finalBalance?'+':''}${money(simAcc.finalBalance-acc.finalBalance)}<\/b><\/div>`;\n      }\n      moSimSlider.oninput=moSimUpdate; moSimUpdate();\n\n      $('rt-copy').onclick=()=>{ const text=`Projected retirement balance at age ${ret}: ${money(acc.finalBalance)}. Total contributed: ${money(acc.totalContributed)}. Calculated on MAFtrix.`; const done=()=>{$('rt-copy').textContent='Copied!';setTimeout(()=>$('rt-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n    }\n\n    function calcFire(){\n      const exp=+$('rt-fire-exp').value, cur=+$('rt-fire-cur').value, mo=+$('rt-fire-mo').value, r=+$('rt-fire-r').value;\n      const coastAge=+$('rt-fire-coastage').value, curAge=+$('rt-fire-age').value;\n      if(exp<=0){ $('rt-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter valid annual expenses.<\/div><\/div>'; return; }\n\n      const regularFire=exp*25, leanFire=exp*0.7*25, fatFire=exp*1.5*25;\n      const monthlyRate=Math.pow(1+r\/100,1\/12)-1;\n      let bal=cur, months=0;\n      while(bal<regularFire &#038;&#038; months<1200){ bal=bal*(1+monthlyRate)+mo; months++; }\n      const yearsToFire=months\/12;\n      const yearsToCoast=Math.max(0,coastAge-curAge);\n      const coastFireNeeded=regularFire\/Math.pow(1+r\/100,yearsToCoast);\n\n      $('rt-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(regularFire)}<\/div><div class=\"dd-main-sub\">Your FIRE number (25\u00d7 annual expenses)<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>FIRE variants<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Type<\/th><th>Target<\/th><th>Basis<\/th><\/tr><\/thead><tbody>\n            <tr><td>Lean FIRE<\/td><td>${money(leanFire)}<\/td><td>70% of current expenses<\/td><\/tr>\n            <tr><td>Regular FIRE<\/td><td>${money(regularFire)}<\/td><td>100% of current expenses<\/td><\/tr>\n            <tr><td>Fat FIRE<\/td><td>${money(fatFire)}<\/td><td>150% of current expenses<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Time to FIRE<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Years to Regular FIRE<\/span><b>${months>=1200?'100+':num(yearsToFire,1)}<\/b><\/div>\n            <div class=\"m-item\"><span>Coast FIRE needed today<\/span><b>${money(coastFireNeeded)}<\/b><\/div>\n          <\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Coast FIRE is the amount needed today such that, with no further contributions, it would grow to your FIRE number by age ${coastAge} through compounding alone.<\/p>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 These are general planning estimates based on the widely-cited 25\u00d7 \/ 4% framework, not personalized financial advice.<\/p>`;\n    }\n\n    function calcLifestyle(){\n      const vals={\n        Housing:+$('rt-ls-housing').value||0, Food:+$('rt-ls-food').value||0,\n        Healthcare:+$('rt-ls-health').value||0, Transportation:+$('rt-ls-transport').value||0,\n        'Travel & entertainment':+$('rt-ls-travel').value||0, Insurance:+$('rt-ls-insurance').value||0,\n        Miscellaneous:+$('rt-ls-misc').value||0\n      };\n      const totalMonthly=Object.values(vals).reduce((a,b)=>a+b,0);\n      $('rt-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(totalMonthly)}<\/div><div class=\"dd-main-sub\">Estimated monthly retirement expenses<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Breakdown<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>${Object.entries(vals).map(([k,v])=>`<tr><td>${k}<\/td><td>${money(v)}<\/td><\/tr>`).join('')}<\/tbody><\/table>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Annual estimate<\/h5><\/div>\n          <div class=\"dd-main-result\" style=\"margin-top:0;\"><div class=\"dd-main-value\">${money(totalMonthly*12)}<\/div><div class=\"dd-main-sub\">Estimated annual retirement expenses<\/div><\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:10px;\">Use this figure as your \"Desired annual retirement income\" in the Savings Projection tab for a fuller readiness picture.<\/p>\n        <\/div>`;\n    }\n\n    calc();\n  }});\n\nreg({slug:'investment',name:'Investment Calculator',cat:'financial',icn:'chart',photo:17,\n  desc:'A complete investment planning center: growth projection with fees and inflation, CAGR\/ROI, lump-sum vs. monthly comparison, goal-based planning, and 3-way scenario comparison.',\n  render(el){\n    function svgLineChart(series,w=560,h=200){\n      if(!series.length) return '';\n      const max=Math.max(...series,1), pad=10;\n      const xOf=i=>pad+(i\/(series.length-1||1))*(w-2*pad);\n      const yOf=v=>h-pad-(v\/max)*(h-2*pad);\n      const d=series.map((v,i)=>`${i===0?'M':'L'}${xOf(i).toFixed(1)},${yOf(v).toFixed(1)}`).join(' ');\n      const area=`${d} L${xOf(series.length-1).toFixed(1)},${h-pad} L${xOf(0).toFixed(1)},${h-pad} Z`;\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\"><path d=\"${area}\" fill=\"rgba(14,165,160,.12)\"\/><path d=\"${d}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/><\/svg>`;\n    }\n    function grow(initial,monthlyContrib,ratePct,years,feePct){\n      const netRate=ratePct-feePct;\n      const monthlyRate=Math.pow(1+netRate\/100,1\/12)-1;\n      const months=Math.round(years*12);\n      let bal=initial, contributed=initial, rows=[];\n      for(let m=1;m<=months;m++){\n        bal=bal*(1+monthlyRate)+monthlyContrib;\n        contributed+=monthlyContrib;\n        if(m%12===0||m===months) rows.push({year:Math.ceil(m\/12),balance:bal,contributed,growth:bal-contributed});\n      }\n      return {finalBalance:bal,totalContributed:contributed,totalGrowth:bal-contributed,rows,months};\n    }\n    const EXAMPLES=[1000,5000,10000,25000,50000];\n    const RISK_PRESETS={conservative:[3,5],moderate:[6,8],aggressive:[9,12]};\n\n    el.innerHTML = `\n      <div class=\"iv-shell\">\n      <div class=\"seg\" id=\"iv-mode-seg\">\n        <button class=\"active\" data-v=\"grow\">Growth calculator<\/button>\n        <button data-v=\"goal\">Goal-based planner<\/button>\n        <button data-v=\"scenario\">Compare 3 scenarios<\/button>\n      <\/div>\n\n      <div id=\"iv-grow-fields\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Initial investment<\/label><input type=\"number\" id=\"iv-init\" value=\"5000\"><\/div>\n          <div class=\"field\"><label>Monthly contribution<\/label><input type=\"number\" id=\"iv-mo\" value=\"250\"><\/div>\n        <\/div>\n        <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Investment period (years)<\/label><input type=\"number\" id=\"iv-y\" value=\"15\"><\/div>\n          <div class=\"field\"><label>Expected annual return (%)<\/label><input type=\"number\" step=\"0.1\" id=\"iv-r\" value=\"8\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>Risk profile <small>(fills a typical illustrative return \u2014 edit freely)<\/small><\/label>\n          <select id=\"iv-risk\"><option value=\"\">Custom<\/option><option value=\"conservative\">Conservative (3-5%)<\/option><option value=\"moderate\">Moderate (6-8%)<\/option><option value=\"aggressive\">Aggressive (9-12%)<\/option><\/select>\n        <\/div>\n        <span class=\"more-link\" id=\"iv-more-link\">+ Inflation &amp; annual fees<\/span>\n        <div class=\"adv-panel more-panel-card\" id=\"iv-more-panel\">\n          <div class=\"form-row\">\n            <div class=\"field\"><label>Expected inflation (%)<\/label><input type=\"number\" step=\"0.1\" id=\"iv-infl\" value=\"3\"><\/div>\n            <div class=\"field\"><label>Annual fee \/ expense ratio (%)<\/label><input type=\"number\" step=\"0.01\" id=\"iv-fee\" value=\"0\"><\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"iv-goal-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Target amount<\/label><input type=\"number\" id=\"iv-goal-amt\" value=\"500000\"><\/div>\n          <div class=\"field\"><label>Initial investment<\/label><input type=\"number\" id=\"iv-goal-init\" value=\"5000\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Expected annual return (%)<\/label><input type=\"number\" step=\"0.1\" id=\"iv-goal-r\" value=\"8\"><\/div>\n          <div class=\"field\"><label>Years to goal<\/label><input type=\"number\" id=\"iv-goal-y\" value=\"20\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"iv-scenario-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label><b>A<\/b> \u2014 Monthly<\/label><input type=\"number\" id=\"iv-sc-a-mo\" value=\"200\"><\/div>\n          <div class=\"field\"><label><b>B<\/b> \u2014 Monthly<\/label><input type=\"number\" id=\"iv-sc-b-mo\" value=\"400\"><\/div>\n          <div class=\"field\"><label><b>C<\/b> \u2014 Monthly<\/label><input type=\"number\" id=\"iv-sc-c-mo\" value=\"600\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Shared initial<\/label><input type=\"number\" id=\"iv-sc-init\" value=\"5000\"><\/div>\n          <div class=\"field\"><label>Shared return (%)<\/label><input type=\"number\" step=\"0.1\" id=\"iv-sc-r\" value=\"8\"><\/div>\n          <div class=\"field\"><label>Shared years<\/label><input type=\"number\" id=\"iv-sc-y\" value=\"20\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"iv-results\"><\/div>\n      <\/div>`;\n\n    let mode='grow';\n    $('iv-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('iv-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('iv-grow-fields').hidden=mode!=='grow';\n      $('iv-goal-fields').hidden=mode!=='goal';\n      $('iv-scenario-fields').hidden=mode!=='scenario';\n      calc();\n    });\n    $('iv-more-link').onclick=()=>$('iv-more-panel').classList.toggle('show');\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('iv-init').value=chip.dataset.v; calc(); });\n    $('iv-risk').onchange=(e)=>{ if(e.target.value){ $('iv-r').value=RISK_PRESETS[e.target.value][1]; calc(); } };\n    el.querySelectorAll('#iv-grow-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#iv-goal-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#iv-scenario-fields input').forEach(i=>i.addEventListener('input',calc));\n\n    function calc(){\n      if(mode==='grow') calcGrow();\n      else if(mode==='goal') calcGoal();\n      else calcScenario();\n    }\n\n    function calcGrow(){\n      const init=+$('iv-init').value, mo=+$('iv-mo').value, y=+$('iv-y').value, r=+$('iv-r').value;\n      const infl=+$('iv-infl').value||0, fee=+$('iv-fee').value||0;\n      if(y<=0){ $('iv-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid investment period.<\/div><\/div>'; return; }\n\n      const proj=grow(init,mo,r,y,0);\n      const projWithFee=fee>0?grow(init,mo,r,y,fee):null;\n      const realValue=proj.finalBalance\/Math.pow(1+infl\/100,y);\n      const roi=((proj.finalBalance-proj.totalContributed)\/proj.totalContributed)*100;\n      const cagr=(Math.pow(proj.finalBalance\/init,1\/y)-1)*100;\n      const lumpSumOnly=init*Math.pow(1+r\/100,y);\n      const monthlyOnly=grow(0,mo,r,y,0).finalBalance;\n\n      $('iv-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(proj.finalBalance)}<\/div><div class=\"dd-main-sub\">Future value after ${y} years<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Performance summary<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Total contributions<\/span><b>${money(proj.totalContributed)}<\/b><\/div>\n            <div class=\"m-item\"><span>Investment growth<\/span><b>${money(proj.totalGrowth)}<\/b><\/div>\n            <div class=\"m-item\"><span>ROI<\/span><b>${num(roi,1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>CAGR (on initial)<\/span><b>${num(cagr,2)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Inflation-adjusted value<\/span><b>${money(realValue)}<\/b><\/div>\n            ${fee>0?`<div class=\"m-item\"><span>Lost to fees (vs. 0% fee)<\/span><b>${money(proj.finalBalance-projWithFee.finalBalance)}<\/b><\/div>`:''}\n          <\/div>\n        <\/div>\n\n        <div class=\"formula-box\"><div class=\"f-label\">Compound growth with contributions<\/div><div class=\"f-eq\">FV = P(1+i)\u207f + PMT \u00d7 [((1+i)\u207f\u22121)\/i]<\/div><\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Principal, contributions &amp; growth<\/h5><\/div>\n          <div id=\"iv-pie\"><\/div>\n          <div id=\"iv-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Growth over time<\/h5><\/div>\n          ${svgLineChart(proj.rows.map(r=>r.balance))}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Year-by-year table<\/h5><\/div>\n          <div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Year<\/th><th>Balance<\/th><th>Contributed<\/th><th>Growth<\/th><\/tr><\/thead><tbody>\n            ${proj.rows.map(rr=>`<tr><td>${rr.year}<\/td><td>${money(rr.balance)}<\/td><td>${money(rr.contributed)}<\/td><td>${money(rr.growth)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Contribution simulator<\/h5><\/div>\n          <div class=\"field\"><label>Monthly contribution: <span id=\"iv-sim-val\">${money(mo)}<\/span><\/label><input type=\"range\" id=\"iv-sim-mo\" min=\"0\" max=\"${Math.max(1000,mo*3)}\" value=\"${mo}\" step=\"10\"><\/div>\n          <div id=\"iv-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Compare return rates<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Return<\/th><th>Future value<\/th><\/tr><\/thead><tbody>\n            ${[4,6,8,10,12].map(rr=>{ const p=grow(init,mo,rr,y,0); return `<tr><td>${rr}%<\/td><td>${money(p.finalBalance)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Compare time horizons<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Years<\/th><th>Future value<\/th><\/tr><\/thead><tbody>\n            ${[5,10,20,30,40].map(yy=>{ const p=grow(init,mo,r,yy,0); return `<tr><td>${yy} years<\/td><td>${money(p.finalBalance)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Lump sum vs. monthly contributions<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            <tr><td>${money(init)} lump sum only (no ongoing contributions)<\/td><td>${money(lumpSumOnly)}<\/td><\/tr>\n            <tr><td>${money(mo)}\/month only (no initial lump sum)<\/td><td>${money(monthlyOnly)}<\/td><\/tr>\n            <tr><td>Combined (your current inputs)<\/td><td>${money(proj.finalBalance)}<\/td><\/tr>\n          <\/tbody><\/table>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">Investing early as a lump sum generally outperforms spreading the same total across monthly contributions, since it has more total time invested \u2014 though consistent contributions are far more accessible for most people building wealth from ongoing income.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Illustrative asset allocation<\/h5><\/div>\n          <div id=\"iv-alloc-pie\"><\/div>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">A generic illustrative example (60% stocks \/ 30% bonds \/ 10% cash) \u2014 not a recommendation. Actual allocation should reflect your own risk tolerance and timeline.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"iv-copy\" type=\"button\">Copy result<\/button>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Calculations are performed in your browser and are not stored. Projections use a constant assumed return \u2014 actual investment returns fluctuate and are never guaranteed.<\/p>`;\n\n      renderPieChart('iv-pie',['Principal','Contributions','Growth'],[+init.toFixed(2),+(proj.totalContributed-init).toFixed(2),+proj.totalGrowth.toFixed(2)],['#4C6FFF','#26C6DA','#F5A623']);\n      $('iv-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Principal <b style=\"margin-left:auto;\">${money(init)}<\/b><\/div><div class=\"m-item\">${dotSpan('#26C6DA')}Contributions <b style=\"margin-left:auto;\">${money(proj.totalContributed-init)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Growth <b style=\"margin-left:auto;\">${money(proj.totalGrowth)}<\/b><\/div>`;\n      renderPieChart('iv-alloc-pie',['Stocks','Bonds','Cash'],[60,30,10],['#4C6FFF','#26C6DA','#94A3B8']);\n\n      const simSlider=$('iv-sim-mo');\n      function simUpdate(){\n        const simMo=+simSlider.value; $('iv-sim-val').textContent=money(simMo);\n        const simProj=grow(init,simMo,r,y,0);\n        $('iv-sim-result').innerHTML = `<div class=\"m-item\"><span>Future value<\/span><b>${money(simProj.finalBalance)}<\/b><\/div><div class=\"m-item\"><span>vs. current setting<\/span><b>${simProj.finalBalance>=proj.finalBalance?'+':''}${money(simProj.finalBalance-proj.finalBalance)}<\/b><\/div>`;\n      }\n      simSlider.oninput=simUpdate; simUpdate();\n\n      $('iv-copy').onclick=()=>{ const text=`Investment future value: ${money(proj.finalBalance)} after ${y} years. Total contributed: ${money(proj.totalContributed)}. Growth: ${money(proj.totalGrowth)}. Calculated on MAFtrix.`; const done=()=>{$('iv-copy').textContent='Copied!';setTimeout(()=>$('iv-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n    }\n\n    function calcGoal(){\n      const goal=+$('iv-goal-amt').value, init=+$('iv-goal-init').value, r=+$('iv-goal-r').value, y=+$('iv-goal-y').value;\n      if(goal<=0||y<=0){ $('iv-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid goal and timeframe.<\/div><\/div>'; return; }\n      const i=Math.pow(1+r\/100,1\/12)-1;\n      const months=Math.round(y*12);\n      const fvInit=init*Math.pow(1+i,months);\n      const remaining=goal-fvInit;\n      const requiredMonthly = remaining<=0 ? 0 : (i===0 ? remaining\/months : remaining\/((Math.pow(1+i,months)-1)\/i));\n      $('iv-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(Math.max(0,requiredMonthly))}<\/div><div class=\"dd-main-sub\">Required monthly contribution to reach ${money(goal)}<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Details<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Goal<\/span><b>${money(goal)}<\/b><\/div>\n            <div class=\"m-item\"><span>Initial investment grows to<\/span><b>${money(fvInit)}<\/b><\/div>\n            <div class=\"m-item\"><span>Still needed from contributions<\/span><b>${money(Math.max(0,remaining))}<\/b><\/div>\n            <div class=\"m-item\"><span>Time<\/span><b>${y} years<\/b><\/div>\n          <\/div>\n          ${remaining<=0?'<p style=\"color:var(--text-soft);font-size:.85rem;margin-top:10px;\">Your initial investment alone is projected to reach this goal \u2014 no additional contribution required.<\/p>':''}\n        <\/div>`;\n    }\n\n    function calcScenario(){\n      const aMo=+$('iv-sc-a-mo').value, bMo=+$('iv-sc-b-mo').value, cMo=+$('iv-sc-c-mo').value;\n      const init=+$('iv-sc-init').value, r=+$('iv-sc-r').value, y=+$('iv-sc-y').value;\n      if(y<=0){ $('iv-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid time period.<\/div><\/div>'; return; }\n      const a=grow(init,aMo,r,y,0), b=grow(init,bMo,r,y,0), c=grow(init,cMo,r,y,0);\n      $('iv-results').innerHTML = `\n        <table class=\"ref-table\"><thead><tr><th>Plan<\/th><th>A (${money(aMo)}\/mo)<\/th><th>B (${money(bMo)}\/mo)<\/th><th>C (${money(cMo)}\/mo)<\/th><\/tr><\/thead><tbody>\n          <tr><td>Final balance<\/td><td>${money(a.finalBalance)}<\/td><td>${money(b.finalBalance)}<\/td><td>${money(c.finalBalance)}<\/td><\/tr>\n          <tr><td>Total contributed<\/td><td>${money(a.totalContributed)}<\/td><td>${money(b.totalContributed)}<\/td><td>${money(c.totalContributed)}<\/td><\/tr>\n          <tr><td>Investment growth<\/td><td>${money(a.totalGrowth)}<\/td><td>${money(b.totalGrowth)}<\/td><td>${money(c.totalGrowth)}<\/td><\/tr>\n          <tr><td>ROI<\/td><td>${num((a.totalGrowth\/a.totalContributed)*100,1)}%<\/td><td>${num((b.totalGrowth\/b.totalContributed)*100,1)}%<\/td><td>${num((c.totalGrowth\/c.totalContributed)*100,1)}%<\/td><\/tr>\n        <\/tbody><\/table>\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:10px;\">All three scenarios share the same initial investment, return rate, and time period \u2014 only the monthly contribution differs.<\/p>`;\n    }\n\n    calc();\n  }});\n\nreg({slug:'amortization',name:'Amortization Calculator',cat:'financial',icn:'ruler',photo:18,\n  desc:'A complete loan repayment analysis tool: full amortization schedule with CSV export, extra-payment simulator, payment-frequency and term comparisons, and a refinance analysis.',\n  render(el){\n    function amortize(principal,annualRatePct,periods,periodsPerYear,extraMonthly){\n      const r=(annualRatePct\/100)\/periodsPerYear;\n      const basePay=r===0?principal\/periods:principal*(r*Math.pow(1+r,periods))\/(Math.pow(1+r,periods)-1);\n      let bal=principal, totalInterest=0, rows=[], p=0;\n      while(bal>0.5 && p<periods*3){\n        p++;\n        const int=bal*r;\n        const princ=Math.min(basePay-int+extraMonthly,bal);\n        bal=Math.max(0,bal-princ);\n        totalInterest+=int;\n        rows.push({period:p,payment:princ+int,principal:princ,interest:int,balance:bal});\n        if(bal<=0.5) break;\n      }\n      return {basePay,totalInterest,rows,periodsUsed:p};\n    }\n    function svgLineChart(series,w=560,h=200){\n      if(!series.length) return '';\n      const max=Math.max(...series,1), pad=10;\n      const xOf=i=>pad+(i\/(series.length-1||1))*(w-2*pad);\n      const yOf=v=>h-pad-(v\/max)*(h-2*pad);\n      const d=series.map((v,i)=>`${i===0?'M':'L'}${xOf(i).toFixed(1)},${yOf(v).toFixed(1)}`).join(' ');\n      const area=`${d} L${xOf(series.length-1).toFixed(1)},${h-pad} L${xOf(0).toFixed(1)},${h-pad} Z`;\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\"><path d=\"${area}\" fill=\"rgba(14,165,160,.12)\"\/><path d=\"${d}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/><\/svg>`;\n    }\n    const EXAMPLES=[10000,50000,100000,250000,500000];\n\n    el.innerHTML = `\n      <div class=\"am-shell\">\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Loan amount<\/label><input type=\"number\" id=\"am-amt\" value=\"240000\"><\/div>\n        <div class=\"field\"><label>Annual rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"am-r\" value=\"6.25\"><\/div>\n      <\/div>\n      <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Term (years)<\/label><input type=\"number\" id=\"am-y\" value=\"30\"><\/div>\n        <div class=\"field\"><label>Payment frequency<\/label><select id=\"am-freq\"><option value=\"12\">Monthly<\/option><option value=\"26\">Bi-weekly<\/option><option value=\"52\">Weekly<\/option><\/select><\/div>\n      <\/div>\n      <span class=\"more-link\" id=\"am-more-link\">+ Extra payments &amp; refinance analysis<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"am-more-panel\">\n        <div class=\"field\"><label>Extra payment per period<\/label><input type=\"number\" id=\"am-extra\" value=\"0\"><\/div>\n        <div class=\"section-tag\">Refinance analysis (optional)<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Years already paid<\/label><input type=\"number\" id=\"am-refi-years\" value=\"5\"><\/div>\n          <div class=\"field\"><label>New rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"am-refi-rate\" value=\"5.5\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>New term (years)<\/label><input type=\"number\" id=\"am-refi-term\" value=\"30\"><\/div>\n          <div class=\"field\"><label>Refinance closing costs<\/label><input type=\"number\" id=\"am-refi-costs\" value=\"3000\"><\/div>\n        <\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"am-btn\" type=\"button\">Build schedule<\/button>\n      <div id=\"am-results\"><\/div>\n      <\/div>`;\n\n    $('am-more-link').onclick=()=>$('am-more-panel').classList.toggle('show');\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('am-amt').value=chip.dataset.v; calc(); });\n    ['am-amt','am-r','am-y','am-freq','am-extra','am-refi-years','am-refi-rate','am-refi-term','am-refi-costs'].forEach(id=>{\n      $(id).addEventListener('input',calc); $(id).addEventListener('change',calc);\n    });\n\n    function calc(){\n      const amt=+$('am-amt').value, r=+$('am-r').value, y=+$('am-y').value;\n      const freq=+$('am-freq').value, extra=+$('am-extra').value||0;\n      if(amt<=0||y<=0){ $('am-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid loan amount and term.<\/div><\/div>'; return; }\n      const periods=Math.round(y*freq);\n      const result=amortize(amt,r,periods,freq,extra);\n      const totalPayment=amt+result.totalInterest;\n      const interestPct=(result.totalInterest\/totalPayment)*100;\n      const freqLabel=freq===12?'month':freq===26?'period (bi-weekly)':'period (weekly)';\n\n      $('am-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(result.basePay+extra)}<\/div><div class=\"dd-main-sub\">Payment per ${freqLabel}<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>Results dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Principal<\/span><b>${money(amt)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total interest<\/span><b>${money(result.totalInterest)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total loan cost<\/span><b>${money(totalPayment)}<\/b><\/div>\n            <div class=\"m-item\"><span>Interest % of total<\/span><b>${num(interestPct,1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Number of payments<\/span><b>${result.periodsUsed}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Principal vs. interest<\/h5><\/div>\n          <div id=\"am-pie\"><\/div>\n          <div id=\"am-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Balance over time<\/h5><\/div>\n          ${svgLineChart(result.rows.filter((_,i)=>i%Math.max(1,Math.round(result.rows.length\/60))===0).map(r=>r.balance))}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Complete amortization schedule<\/h5><\/div>\n          <div class=\"seg\" id=\"am-sched-seg\"><button class=\"active\" data-v=\"annual\">Annual summary<\/button><button data-v=\"all\">Every payment<\/button><\/div>\n          <div class=\"field\" id=\"am-search-field\" hidden><label>Search by period #<\/label><input type=\"number\" id=\"am-search\" placeholder=\"e.g. 24\"><\/div>\n          <div id=\"am-schedule\"><\/div>\n          <button class=\"btn btn-ghost btn-sm\" id=\"am-csv\" type=\"button\" style=\"margin-top:10px;\">Download CSV<\/button>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Extra payment simulator<\/h5><\/div>\n          <div class=\"field\"><label>Extra per ${freqLabel}: <span id=\"am-sim-val\">${money(extra)}<\/span><\/label><input type=\"range\" id=\"am-sim-extra\" min=\"0\" max=\"${Math.round(result.basePay)}\" value=\"${extra}\"><\/div>\n          <div id=\"am-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Payment frequency comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Frequency<\/th><th>Payment<\/th><th>Total interest<\/th><th>Payoff time<\/th><\/tr><\/thead><tbody>\n            ${[['Monthly',12],['Bi-weekly',26],['Weekly',52]].map(([label,f])=>{ const p=Math.round(y*f); const rr=amortize(amt,r,p,f,0); return `<tr><td>${label}<\/td><td>${money(rr.basePay)}<\/td><td>${money(rr.totalInterest)}<\/td><td>${num(rr.periodsUsed\/f,1)} years<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Loan term comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Term<\/th><th>Payment<\/th><th>Total interest<\/th><\/tr><\/thead><tbody>\n            ${[10,15,20,25,30,40].map(tt=>{ const p=Math.round(tt*freq); const rr=amortize(amt,r,p,freq,0); return `<tr><td>${tt} years<\/td><td>${money(rr.basePay)}<\/td><td>${money(rr.totalInterest)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Interest rate comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Rate<\/th><th>Payment<\/th><th>Total interest<\/th><\/tr><\/thead><tbody>\n            ${[r-2,r-1,r,r+1,r+2].filter(rr=>rr>0).map(rr=>{ const p=Math.round(y*freq); const res=amortize(amt,rr,p,freq,0); return `<tr><td>${num(rr,2)}%<\/td><td>${money(res.basePay)}<\/td><td>${money(res.totalInterest)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        ${(+$('am-refi-years').value)>0?(()=>{\n          const refiYears=+$('am-refi-years').value, newRate=+$('am-refi-rate').value, newTerm=+$('am-refi-term').value, closingCosts=+$('am-refi-costs').value||0;\n          const paidPeriods=Math.round(refiYears*freq);\n          if(paidPeriods>=result.periodsUsed) return '';\n          const remainingBalance=result.rows[Math.min(paidPeriods,result.rows.length-1)]?.balance ?? amt;\n          const newLoan=amortize(remainingBalance,newRate,Math.round(newTerm*12),12,0);\n          const oldRemainingPayments=result.periodsUsed-paidPeriods;\n          const oldRemainingCost=result.basePay*oldRemainingPayments;\n          const newTotalCost=newLoan.basePay*newLoan.periodsUsed+closingCosts;\n          const monthlySavings=result.basePay-newLoan.basePay;\n          const breakEvenMonths=monthlySavings>0?Math.ceil(closingCosts\/monthlySavings):null;\n          return `<div class=\"calc-section section-blue\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Refinance analysis<\/h5><\/div>\n            <p style=\"color:var(--text-soft);font-size:.85rem;\">After ${refiYears} years, remaining balance \u2248 ${money(remainingBalance)}. Refinancing into a ${newTerm}-year loan at ${newRate}%:<\/p>\n            <table class=\"ref-table\"><thead><tr><th><\/th><th>Keep current loan<\/th><th>Refinance<\/th><\/tr><\/thead><tbody>\n              <tr><td>Monthly payment<\/td><td>${money(result.basePay)}<\/td><td>${money(newLoan.basePay)}<\/td><\/tr>\n              <tr><td>Remaining cost<\/td><td>${money(oldRemainingCost)}<\/td><td>${money(newTotalCost)}<\/td><\/tr>\n            <\/tbody><\/table>\n            <div class=\"gpa-insight\" style=\"background:${monthlySavings>0?'#0EA5A018':'#E0533D18'};border:1px solid ${monthlySavings>0?'#0EA5A044':'#E0533D44'};margin-top:12px;\">\n              ${monthlySavings>0?`Refinancing saves ${money(monthlySavings)}\/month.${breakEvenMonths?` With ${money(closingCosts)} in closing costs, you'd break even in about ${breakEvenMonths} months.`:''}`:`The new payment (${money(newLoan.basePay)}) is higher than your current payment \u2014 refinancing wouldn't reduce your monthly cost here, though total cost may still differ.`}\n            <\/div>\n          <\/div>`; })():''}\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"am-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"am-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Calculations are performed in your browser and are not stored.<\/p>`;\n\n      renderPieChart('am-pie',['Principal','Total interest'],[+amt.toFixed(2),+result.totalInterest.toFixed(2)],['#4C6FFF','#F5A623']);\n      $('am-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Principal <b style=\"margin-left:auto;\">${money(amt)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Total interest <b style=\"margin-left:auto;\">${money(result.totalInterest)}<\/b><\/div>`;\n\n      function renderSchedule(mode,searchPeriod){\n        if(mode==='annual'){\n          $('am-search-field').hidden=true;\n          const yearly=[]; let curYear=1,yPrinc=0,yInt=0,count=0,bal=amt;\n          result.rows.forEach(r=>{ yPrinc+=r.principal; yInt+=r.interest; bal=r.balance; count++; if(count>=freq){ yearly.push({year:curYear,principal:yPrinc,interest:yInt,balance:bal}); curYear++; yPrinc=0;yInt=0;count=0; } });\n          if(count>0) yearly.push({year:curYear,principal:yPrinc,interest:yInt,balance:bal});\n          $('am-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>Year<\/th><th>Principal<\/th><th>Interest<\/th><th>Ending balance<\/th><\/tr><\/thead><tbody>${yearly.map(y2=>`<tr><td>${y2.year}<\/td><td>${money(y2.principal)}<\/td><td>${money(y2.interest)}<\/td><td>${money(y2.balance)}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`;\n        } else {\n          $('am-search-field').hidden=false;\n          let rowsToShow=result.rows;\n          if(searchPeriod) rowsToShow=result.rows.filter(r=>r.period===searchPeriod);\n          $('am-schedule').innerHTML = `<div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>#<\/th><th>Payment<\/th><th>Principal<\/th><th>Interest<\/th><th>Balance<\/th><\/tr><\/thead><tbody>${rowsToShow.slice(0,500).map(r=>`<tr><td>${r.period}<\/td><td>${money(r.payment)}<\/td><td>${money(r.principal)}<\/td><td>${money(r.interest)}<\/td><td>${money(r.balance)}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>${rowsToShow.length>500?'<p style=\"font-size:.75rem;color:var(--text-soft);\">Showing first 500 rows \u2014 use search or CSV export for the full schedule.<\/p>':''}`;\n        }\n      }\n      let currentSchedMode='annual';\n      renderSchedule('annual');\n      $('am-sched-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{ $('am-sched-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); currentSchedMode=b.dataset.v; renderSchedule(currentSchedMode); });\n      $('am-search')?.addEventListener?.('input',()=>{});\n      const searchInput=document.getElementById('am-search');\n      if(searchInput) searchInput.oninput=()=>{ renderSchedule('all', +searchInput.value||null); };\n\n      $('am-csv').onclick=()=>{\n        const csv='Period,Payment,Principal,Interest,Balance\\n'+result.rows.map(r=>`${r.period},${r.payment.toFixed(2)},${r.principal.toFixed(2)},${r.interest.toFixed(2)},${r.balance.toFixed(2)}`).join('\\n');\n        const blob=new Blob([csv],{type:'text\/csv'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='amortization-schedule.csv'; a.click(); URL.revokeObjectURL(url);\n      };\n\n      const simSlider=$('am-sim-extra');\n      function simUpdate(){\n        const simExtra=+simSlider.value; $('am-sim-val').textContent=money(simExtra);\n        const simResult=amortize(amt,r,periods,freq,simExtra);\n        const interestSaved=result.totalInterest-simResult.totalInterest;\n        const periodsSaved=result.periodsUsed-simResult.periodsUsed;\n        $('am-sim-result').innerHTML = `<div class=\"m-item\"><span>Interest saved<\/span><b>${money(interestSaved)}<\/b><\/div><div class=\"m-item\"><span>Time saved<\/span><b>${(periodsSaved\/freq).toFixed(1)} years<\/b><\/div>`;\n      }\n      simSlider.oninput=simUpdate; simUpdate();\n\n      $('am-copy').onclick=()=>{ const text=`Amortization: ${money(result.basePay+extra)}\/period, ${money(result.totalInterest)} total interest, ${money(totalPayment)} total cost. Calculated on MAFtrix.`; const done=()=>{$('am-copy').textContent='Copied!';setTimeout(()=>$('am-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n      $('am-print').onclick=()=>window.print();\n    }\n    $('am-btn').onclick=calc;\n    calc();\n  }});\n\nreg({slug:'sales-tax',name:'Sales Tax Calculator',cat:'financial',icn:'percent',photo:19,\n  desc:'A complete sales tax toolkit: itemized receipts with discounts and shipping, a multi-item cart calculator, tax-rate finder, and a shopping budget planner.',\n  render(el){\n    const EXAMPLES=[10,50,100,500,1000];\n\n    el.innerHTML = `\n      <div class=\"st-shell\">\n      <div class=\"seg\" id=\"st-mode-seg\">\n        <button class=\"active\" data-v=\"single\">Single item<\/button>\n        <button data-v=\"cart\">Multi-item cart<\/button>\n        <button data-v=\"findrate\">Find tax rate<\/button>\n        <button data-v=\"budget\">Shopping budget<\/button>\n      <\/div>\n\n      <div id=\"st-single-fields\">\n        <div class=\"seg\" id=\"st-seg\"><button class=\"active\" data-v=\"add\">Price before tax<\/button><button data-v=\"reverse\">Price including tax<\/button><\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label id=\"st-price-label\">Price before tax<\/label><input type=\"number\" id=\"st-price\" value=\"80\"><\/div>\n          <div class=\"field\"><label>Tax rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"st-rate\" value=\"8.25\"><\/div>\n        <\/div>\n        <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n        <span class=\"more-link\" id=\"st-more-link\">+ Discount, quantity &amp; shipping<\/span>\n        <div class=\"adv-panel more-panel-card\" id=\"st-more-panel\">\n          <div class=\"form-row\">\n            <div class=\"field\"><label>Quantity<\/label><input type=\"number\" id=\"st-qty\" value=\"1\" min=\"1\"><\/div>\n            <div class=\"field\"><label>Discount (%)<\/label><input type=\"number\" id=\"st-discount\" value=\"0\"><\/div>\n          <\/div>\n          <div class=\"field\"><label>Shipping \/ handling (not taxed)<\/label><input type=\"number\" id=\"st-shipping\" value=\"0\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"st-cart-fields\" hidden>\n        <div id=\"st-cart-rows\"><\/div>\n        <button class=\"btn btn-ghost btn-sm\" id=\"st-cart-add\" type=\"button\" style=\"margin-bottom:12px;\">+ Add item<\/button>\n        <div class=\"field\"><label>Tax rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"st-cart-rate\" value=\"8.25\"><\/div>\n      <\/div>\n\n      <div id=\"st-findrate-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Original price<\/label><input type=\"number\" id=\"st-fr-orig\" value=\"100\"><\/div>\n          <div class=\"field\"><label>Final price (tax included)<\/label><input type=\"number\" id=\"st-fr-final\" value=\"108.25\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"st-budget-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Total budget<\/label><input type=\"number\" id=\"st-bg-budget\" value=\"500\"><\/div>\n          <div class=\"field\"><label>Tax rate (%)<\/label><input type=\"number\" step=\"0.01\" id=\"st-bg-rate\" value=\"8\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"st-results\"><\/div>\n      <\/div>`;\n\n    let mode='single', taxMode='add';\n    $('st-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('st-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('st-single-fields').hidden=mode!=='single';\n      $('st-cart-fields').hidden=mode!=='cart';\n      $('st-findrate-fields').hidden=mode!=='findrate';\n      $('st-budget-fields').hidden=mode!=='budget';\n      calc();\n    });\n    $('st-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('st-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); taxMode=b.dataset.v;\n      $('st-price-label').textContent = taxMode==='add' ? 'Price before tax' : 'Total price (tax included)';\n      calc();\n    });\n    $('st-more-link').onclick=()=>$('st-more-panel').classList.toggle('show');\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('st-price').value=chip.dataset.v; calc(); });\n    el.querySelectorAll('#st-single-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#st-findrate-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#st-budget-fields input').forEach(i=>i.addEventListener('input',calc));\n\n    function addCartRow(name,price,qty){\n      const row=document.createElement('div');\n      row.className='st-cart-row';\n      row.innerHTML = `<input type=\"text\" class=\"st-c-name\" placeholder=\"Item name\" value=\"${name||''}\"><input type=\"number\" class=\"st-c-price\" placeholder=\"Price\" value=\"${price!==undefined?price:''}\"><input type=\"number\" class=\"st-c-qty\" placeholder=\"Qty\" value=\"${qty||1}\" min=\"1\"><span><\/span><button class=\"btn btn-ghost btn-sm\" type=\"button\" onclick=\"this.closest('.st-cart-row').remove();window.dispatchEvent(new Event('st-cart-change'))\">\u2715<\/button>`;\n      $('st-cart-rows').appendChild(row);\n      row.querySelectorAll('input').forEach(i=>i.addEventListener('input',calc));\n    }\n    addCartRow('T-shirt',24.99,2);\n    addCartRow('Sneakers',89.00,1);\n    $('st-cart-add').onclick=()=>{ addCartRow('',undefined,1); };\n    window.addEventListener('st-cart-change',calc);\n    $('st-cart-rate').addEventListener('input',calc);\n\n    function calc(){\n      if(mode==='single') calcSingle();\n      else if(mode==='cart') calcCart();\n      else if(mode==='findrate') calcFindRate();\n      else calcBudget();\n    }\n\n    function calcSingle(){\n      const priceInput=+$('st-price').value, rate=+$('st-rate').value;\n      const qty=+$('st-qty').value||1, discountPct=+$('st-discount').value||0, shipping=+$('st-shipping').value||0;\n      let base,tax,total;\n      if(taxMode==='add'){\n        const listTotal=priceInput*qty;\n        const discountAmt=listTotal*discountPct\/100;\n        const taxable=listTotal-discountAmt;\n        tax=taxable*rate\/100;\n        base=taxable;\n        total=taxable+tax+shipping;\n        renderSingleResult(listTotal,discountAmt,taxable,tax,shipping,total,rate);\n      } else {\n        const totalIncl=priceInput*qty;\n        base=totalIncl\/(1+rate\/100);\n        tax=totalIncl-base;\n        total=totalIncl+shipping;\n        renderSingleResult(totalIncl,0,base,tax,shipping,total,rate,true);\n      }\n    }\n\n    function renderSingleResult(listTotal,discountAmt,taxable,tax,shipping,total,rate,isReverse){\n      const discountBeforeTax=discountAmt>0?tax:0;\n      const taxIfNoDiscount=listTotal*rate\/100;\n      const savingsFromDiscountOrder=taxIfNoDiscount-tax;\n\n      $('st-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(total)}<\/div><div class=\"dd-main-sub\">${isReverse?'Total (including shipping)':'Final price'}<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Itemized receipt<\/h5><\/div>\n          <div class=\"st-receipt\">\n            <div class=\"rline\"><span>Item price${discountAmt>0?' (before discount)':''}<\/span><span>${money(listTotal)}<\/span><\/div>\n            ${discountAmt>0?`<div class=\"rline\"><span>Discount<\/span><span>\u2212${money(discountAmt)}<\/span><\/div><div class=\"rline\"><span>Taxable amount<\/span><span>${money(taxable)}<\/span><\/div>`:''}\n            <div class=\"rline\"><span>Sales tax (${rate}%)<\/span><span>${money(tax)}<\/span><\/div>\n            ${shipping>0?`<div class=\"rline\"><span>Shipping\/handling (not taxed)<\/span><span>${money(shipping)}<\/span><\/div>`:''}\n            <div class=\"rline total\"><span>Grand total<\/span><span>${money(total)}<\/span><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Price breakdown<\/h5><\/div>\n          <div id=\"st-pie\"><\/div>\n          <div id=\"st-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        ${discountAmt>0?`<div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Discount timing comparison<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            <tr><td>Tax if discount applied before tax (this calculation)<\/td><td>${money(tax)}<\/td><\/tr>\n            <tr><td>Tax if discount applied after tax (on full price)<\/td><td>${money(taxIfNoDiscount)}<\/td><\/tr>\n          <\/tbody><\/table>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Applying a discount before calculating tax (the more common approach) saves you ${money(savingsFromDiscountOrder)} in this case, since tax is charged on the lower, discounted amount.<\/p>\n        <\/div>`:''}\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Compare tax rates<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Rate<\/th><th>Tax<\/th><th>Total<\/th><\/tr><\/thead><tbody>\n            ${[5,7,8,10,15].map(rr=>{ const t=taxable*rr\/100; return `<tr><td>${rr}%<\/td><td>${money(t)}<\/td><td>${money(taxable+t+shipping)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"st-copy\" type=\"button\">Copy result<\/button>\n        <\/div>`;\n\n      renderPieChart('st-pie',['Pre-tax amount','Sales tax'].concat(shipping>0?['Shipping']:[]),[+taxable.toFixed(2),+tax.toFixed(2)].concat(shipping>0?[+shipping.toFixed(2)]:[]),['#4C6FFF','#F5A623','#26C6DA']);\n      $('st-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Pre-tax <b style=\"margin-left:auto;\">${money(taxable)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Tax <b style=\"margin-left:auto;\">${money(tax)}<\/b><\/div>${shipping>0?`<div class=\"m-item\">${dotSpan('#26C6DA')}Shipping <b style=\"margin-left:auto;\">${money(shipping)}<\/b><\/div>`:''}`;\n      $('st-copy').onclick=()=>{ const text=`Total: ${money(total)} (tax: ${money(tax)} at ${rate}%). Calculated on MAFtrix.`; const done=()=>{$('st-copy').textContent='Copied!';setTimeout(()=>$('st-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n    }\n\n    function calcCart(){\n      const rate=+$('st-cart-rate').value||0;\n      const rows=[...$('st-cart-rows').children].map(row=>({\n        name:row.querySelector('.st-c-name').value||'Item',\n        price:+row.querySelector('.st-c-price').value||0,\n        qty:+row.querySelector('.st-c-qty').value||1\n      })).filter(r=>r.price>0);\n      if(!rows.length){ $('st-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your cart<\/div><div class=\"r-value\">Add at least one item with a price.<\/div><\/div>'; return; }\n      const subtotal=rows.reduce((a,r)=>a+r.price*r.qty,0);\n      const tax=subtotal*rate\/100;\n      const total=subtotal+tax;\n      $('st-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(total)}<\/div><div class=\"dd-main-sub\">Cart total (${rows.length} item${rows.length>1?'s':''})<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Itemized cart<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Item<\/th><th>Price<\/th><th>Qty<\/th><th>Line total<\/th><\/tr><\/thead><tbody>\n            ${rows.map(r=>`<tr><td>${r.name}<\/td><td>${money(r.price)}<\/td><td>${r.qty}<\/td><td>${money(r.price*r.qty)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table>\n          <div class=\"st-receipt\" style=\"margin-top:14px;\">\n            <div class=\"rline\"><span>Subtotal<\/span><span>${money(subtotal)}<\/span><\/div>\n            <div class=\"rline\"><span>Sales tax (${rate}%)<\/span><span>${money(tax)}<\/span><\/div>\n            <div class=\"rline total\"><span>Grand total<\/span><span>${money(total)}<\/span><\/div>\n          <\/div>\n        <\/div>`;\n    }\n\n    function calcFindRate(){\n      const orig=+$('st-fr-orig').value, final=+$('st-fr-final').value;\n      if(orig<=0){ $('st-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid original price.<\/div><\/div>'; return; }\n      const tax=final-orig;\n      const rate=(tax\/orig)*100;\n      $('st-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(rate,3)}%<\/div><div class=\"dd-main-sub\">Implied sales tax rate<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">Tax amount = ${money(final)} \u2212 ${money(orig)} = ${money(tax)}<br>Rate = ${money(tax)} \u00f7 ${money(orig)} \u00d7 100 = ${num(rate,3)}%<\/p>\n        <\/div>`;\n    }\n\n    function calcBudget(){\n      const budget=+$('st-bg-budget').value, rate=+$('st-bg-rate').value;\n      if(budget<=0){ $('st-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid budget.<\/div><\/div>'; return; }\n      const maxPreTax=budget\/(1+rate\/100);\n      const taxAtMax=budget-maxPreTax;\n      $('st-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(maxPreTax)}<\/div><div class=\"dd-main-sub\">Maximum pre-tax purchase amount<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Details<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Your budget<\/span><b>${money(budget)}<\/b><\/div>\n            <div class=\"m-item\"><span>Max pre-tax price<\/span><b>${money(maxPreTax)}<\/b><\/div>\n            <div class=\"m-item\"><span>Sales tax at that price<\/span><b>${money(taxAtMax)}<\/b><\/div>\n          <\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Useful when shopping with a fixed budget \u2014 this is the highest sticker price you can choose so that price + tax doesn't exceed your budget.<\/p>\n        <\/div>`;\n    }\n\n    calc();\n  }});\n\nreg({slug:'tip',name:'Tip Calculator',cat:'financial',icn:'ticket',photo:20,\n  desc:'A complete dining and tipping assistant: instant tip and split calculation, unequal bill splitting, reverse tip finder, and a country tipping guide.',\n  render(el){\n    const QUICK_TIPS=[10,15,18,20,25,'Custom'];\n    const SERVICE_RANGES={Poor:[0,5],Fair:[8,12],Good:[15,18],Great:[18,22],Excellent:[22,25]};\n    const COUNTRY_GUIDE=[\n      ['United States','Often 15-20% at restaurants; tipping is widely expected in many service industries.'],\n      ['Canada','Similar to the US \u2014 often 15-20% at restaurants.'],\n      ['United Kingdom','Often 10-15% where a service charge is not already included.'],\n      ['France','A service charge is often included by law; small additional rounding is optional.'],\n      ['Germany','Rounding up or a small addition (often around 5-10%) is common.'],\n      ['Italy','A small amount is sometimes appreciated; service charges may already be included.'],\n      ['Japan','Tipping is generally not customary and can occasionally be perceived as unusual.'],\n      ['Australia','Not traditionally expected, though rounding up is increasingly common in cities.'],\n      ['UAE','A service charge is often included; additional tipping is optional and appreciated.'],\n      ['Pakistan \/ India','Often 5-10% where not already included, though practices vary regionally.']\n    ];\n\n    el.innerHTML = `\n      <div class=\"tp-shell\">\n      <div class=\"seg\" id=\"tp-mode-seg\">\n        <button class=\"active\" data-v=\"standard\">Split &amp; tip<\/button>\n        <button data-v=\"unequal\">Unequal split<\/button>\n        <button data-v=\"reverse\">Reverse tip<\/button>\n        <button data-v=\"guide\">Tipping guide<\/button>\n      <\/div>\n\n      <div id=\"tp-standard-fields\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Bill amount<\/label><input type=\"number\" id=\"tp-bill\" value=\"64\"><\/div>\n          <div class=\"field\"><label>Number of people<\/label><input type=\"number\" id=\"tp-people\" value=\"2\" min=\"1\"><\/div>\n        <\/div>\n        <div class=\"tp-quick-grid\" id=\"tp-quick-grid\">${QUICK_TIPS.map((t,i)=>`<div class=\"tp-quick-btn${i===2?' active':''}\" data-t=\"${t}\">${t==='Custom'?'Custom':t+'%'}<\/div>`).join('')}<\/div>\n        <div class=\"field\"><label>Tip %: <span id=\"tp-slider-val\">18<\/span>%<\/label><input type=\"range\" id=\"tp-slider\" min=\"0\" max=\"35\" value=\"18\"><\/div>\n        <span class=\"more-link\" id=\"tp-more-link\">+ Tax, rounding &amp; service quality<\/span>\n        <div class=\"adv-panel more-panel-card\" id=\"tp-more-panel\">\n          <div class=\"field\"><label>Tax amount (optional)<\/label><input type=\"number\" id=\"tp-tax\" value=\"0\"><\/div>\n          <div class=\"seg\" id=\"tp-taxmode-seg\"><button class=\"active\" data-v=\"before\">Tip before tax<\/button><button data-v=\"after\">Tip after tax<\/button><\/div>\n          <div class=\"field\"><label>Rounding<\/label><select id=\"tp-round\"><option value=\"none\">No rounding<\/option><option value=\"tipup\">Round tip up to nearest dollar<\/option><option value=\"totalup\">Round total up to nearest dollar<\/option><\/select><\/div>\n          <div class=\"field\"><label>Service quality<\/label><select id=\"tp-quality\"><option value=\"\">Select...<\/option>${Object.keys(SERVICE_RANGES).map(k=>`<option value=\"${k}\">${k}<\/option>`).join('')}<\/select><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"tp-unequal-fields\" hidden>\n        <div id=\"tp-person-rows\"><\/div>\n        <button class=\"btn btn-ghost btn-sm\" id=\"tp-person-add\" type=\"button\" style=\"margin-bottom:12px;\">+ Add person<\/button>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Tip %<\/label><input type=\"number\" id=\"tp-uneq-tip\" value=\"18\"><\/div>\n          <div class=\"field\"><label>Tax %<\/label><input type=\"number\" id=\"tp-uneq-tax\" value=\"0\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"tp-reverse-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Total amount paid<\/label><input type=\"number\" id=\"tp-rev-total\" value=\"94.40\"><\/div>\n          <div class=\"field\"><label>Original bill (before tip)<\/label><input type=\"number\" id=\"tp-rev-bill\" value=\"80\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"tp-guide-fields\" hidden><\/div>\n\n      <div id=\"tp-results\"><\/div>\n      <\/div>`;\n\n    let mode='standard', taxMode='before';\n    $('tp-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('tp-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('tp-standard-fields').hidden=mode!=='standard';\n      $('tp-unequal-fields').hidden=mode!=='unequal';\n      $('tp-reverse-fields').hidden=mode!=='reverse';\n      $('tp-guide-fields').hidden=mode!=='guide';\n      calc();\n    });\n    $('tp-more-link').onclick=()=>$('tp-more-panel').classList.toggle('show');\n    $('tp-taxmode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{ $('tp-taxmode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); taxMode=b.dataset.v; calc(); });\n    $('tp-quick-grid').querySelectorAll('.tp-quick-btn').forEach(b=>b.onclick=()=>{\n      $('tp-quick-grid').querySelectorAll('.tp-quick-btn').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      if(b.dataset.t!=='Custom'){ $('tp-slider').value=b.dataset.t; $('tp-slider-val').textContent=b.dataset.t; }\n      calc();\n    });\n    $('tp-slider').oninput=(e)=>{ $('tp-slider-val').textContent=e.target.value; $('tp-quick-grid').querySelectorAll('.tp-quick-btn').forEach(x=>x.classList.remove('active')); const match=[...$('tp-quick-grid').querySelectorAll('.tp-quick-btn')].find(x=>x.dataset.t===e.target.value); if(match) match.classList.add('active'); else $('tp-quick-grid').querySelector('[data-t=\"Custom\"]').classList.add('active'); calc(); };\n    $('tp-round').addEventListener('change',calc);\n    $('tp-quality').onchange=(e)=>{ if(e.target.value){ const range=SERVICE_RANGES[e.target.value]; const mid=Math.round((range[0]+range[1])\/2); $('tp-slider').value=mid; $('tp-slider-val').textContent=mid; calc(); } };\n    el.querySelectorAll('#tp-standard-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#tp-reverse-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#tp-unequal-fields input').forEach(i=>i.addEventListener('input',calc));\n\n    function addPersonRow(name,amt){\n      const row=document.createElement('div');\n      row.className='tp-person-row';\n      row.innerHTML=`<input type=\"text\" class=\"tp-p-name\" placeholder=\"Name\" value=\"${name||''}\"><input type=\"number\" class=\"tp-p-amt\" placeholder=\"Amount\" value=\"${amt!==undefined?amt:''}\"><button class=\"btn btn-ghost btn-sm\" type=\"button\" onclick=\"this.closest('.tp-person-row').remove();window.dispatchEvent(new Event('tp-person-change'))\">\u2715<\/button>`;\n      $('tp-person-rows').appendChild(row);\n      row.querySelectorAll('input').forEach(i=>i.addEventListener('input',calc));\n    }\n    addPersonRow('Alex',35); addPersonRow('Sam',20); addPersonRow('Jordan',55);\n    $('tp-person-add').onclick=()=>addPersonRow('',undefined);\n    window.addEventListener('tp-person-change',calc);\n\n    function calc(){\n      if(mode==='standard') calcStandard();\n      else if(mode==='unequal') calcUnequal();\n      else if(mode==='reverse') calcReverse();\n      else renderGuide();\n    }\n\n    function calcStandard(){\n      const bill=+$('tp-bill').value, people=Math.max(1,+$('tp-people').value), pct=+$('tp-slider').value;\n      const tax=+$('tp-tax').value||0, roundMode=$('tp-round').value;\n      if(bill<=0){ $('tp-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid bill amount.<\/div><\/div>'; return; }\n      const tipBase=taxMode==='before'?bill:bill+tax;\n      let tip=tipBase*pct\/100;\n      let total=bill+tax+tip;\n      if(roundMode==='tipup'){ tip=Math.ceil(tip); total=bill+tax+tip; }\n      else if(roundMode==='totalup'){ const roundedTotal=Math.ceil(total); tip=roundedTotal-bill-tax; total=roundedTotal; }\n\n      $('tp-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(tip)}<\/div><div class=\"dd-main-sub\">Tip amount (${pct}%)<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Results dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Bill<\/span><b>${money(bill)}<\/b><\/div>\n            ${tax>0?`<div class=\"m-item\"><span>Tax<\/span><b>${money(tax)}<\/b><\/div>`:''}\n            <div class=\"m-item\"><span>Tip<\/span><b>${money(tip)}<\/b><\/div>\n            <div class=\"m-item\"><span>Grand total<\/span><b>${money(total)}<\/b><\/div>\n            <div class=\"m-item\"><span>Per person (tip)<\/span><b>${money(tip\/people)}<\/b><\/div>\n            <div class=\"m-item\"><span>Per person (total)<\/span><b>${money(total\/people)}<\/b><\/div>\n          <\/div>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Bill breakdown<\/h5><\/div>\n          <div id=\"tp-pie\"><\/div>\n          <div id=\"tp-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Compare tip percentages<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Tip %<\/th><th>Tip amount<\/th><th>Total<\/th><\/tr><\/thead><tbody>\n            ${[10,15,18,20,25].map(p=>{ const t=tipBase*p\/100; return `<tr><td>${p}%<\/td><td>${money(t)}<\/td><td>${money(bill+tax+t)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n        ${people>1?`<div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Split among ${people} people<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>${Array.from({length:people},(_,i)=>`<tr><td>Person ${i+1}<\/td><td>${money(total\/people)}<\/td><\/tr>`).join('')}<\/tbody><\/table>\n        <\/div>`:''}\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"tp-copy\" type=\"button\">Copy result<\/button>\n        <\/div>`;\n      renderPieChart('tp-pie',['Bill','Tip'].concat(tax>0?['Tax']:[]),[+bill.toFixed(2),+tip.toFixed(2)].concat(tax>0?[+tax.toFixed(2)]:[]),['#4C6FFF','#F5A623','#26C6DA']);\n      $('tp-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Bill <b style=\"margin-left:auto;\">${money(bill)}<\/b><\/div><div class=\"m-item\">${dotSpan('#F5A623')}Tip <b style=\"margin-left:auto;\">${money(tip)}<\/b><\/div>${tax>0?`<div class=\"m-item\">${dotSpan('#26C6DA')}Tax <b style=\"margin-left:auto;\">${money(tax)}<\/b><\/div>`:''}`;\n      $('tp-copy').onclick=()=>{ const text=`Tip: ${money(tip)} (${pct}%). Total: ${money(total)}. ${people>1?`Per person: ${money(total\/people)}. `:''}Calculated on MAFtrix.`; const done=()=>{$('tp-copy').textContent='Copied!';setTimeout(()=>$('tp-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n    }\n\n    function calcUnequal(){\n      const tipPct=+$('tp-uneq-tip').value||0, taxPct=+$('tp-uneq-tax').value||0;\n      const people=[...$('tp-person-rows').children].map(row=>({\n        name:row.querySelector('.tp-p-name').value||'Person',\n        amt:+row.querySelector('.tp-p-amt').value||0\n      })).filter(p=>p.amt>0);\n      if(!people.length){ $('tp-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Add at least one person with an amount.<\/div><\/div>'; return; }\n      const subtotal=people.reduce((a,p)=>a+p.amt,0);\n      const totalTax=subtotal*taxPct\/100, totalTip=subtotal*tipPct\/100;\n      const grandTotal=subtotal+totalTax+totalTip;\n      $('tp-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(grandTotal)}<\/div><div class=\"dd-main-sub\">Grand total for ${people.length} people<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Each person owes<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Person<\/th><th>Their order<\/th><th>Share of tax+tip<\/th><th>Total owed<\/th><\/tr><\/thead><tbody>\n            ${people.map(p=>{ const share=p.amt\/subtotal; const theirTax=totalTax*share, theirTip=totalTip*share; return `<tr><td>${p.name}<\/td><td>${money(p.amt)}<\/td><td>${money(theirTax+theirTip)}<\/td><td>${money(p.amt+theirTax+theirTip)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Tax and tip are split proportionally to what each person ordered \u2014 a fairer approach than an equal split when orders differ significantly.<\/p>\n        <\/div>`;\n    }\n\n    function calcReverse(){\n      const total=+$('tp-rev-total').value, bill=+$('tp-rev-bill').value;\n      if(bill<=0){ $('tp-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid bill amount.<\/div><\/div>'; return; }\n      const tip=total-bill;\n      const pct=(tip\/bill)*100;\n      $('tp-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(pct,1)}%<\/div><div class=\"dd-main-sub\">Implied tip percentage<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Details<\/h5><\/div>\n          <div class=\"summary-mini\"><div class=\"m-item\"><span>Bill<\/span><b>${money(bill)}<\/b><\/div><div class=\"m-item\"><span>Tip amount<\/span><b>${money(tip)}<\/b><\/div><div class=\"m-item\"><span>Total paid<\/span><b>${money(total)}<\/b><\/div><\/div>\n        <\/div>`;\n    }\n\n    function renderGuide(){\n      $('tp-results').innerHTML = `\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Tipping customs by country<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>${COUNTRY_GUIDE.map(([c,d])=>`<tr><td><b>${c}<\/b><\/td><td>${d}<\/td><\/tr>`).join('')}<\/tbody><\/table>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:10px;\">General guidance only \u2014 customs vary by region, establishment and situation. When in doubt, local norms or venue staff are the best source.<\/p>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Tipping by service type<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            <tr><td>Restaurant<\/td><td>Often 15-20% in the US where not already included<\/td><\/tr>\n            <tr><td>Food delivery<\/td><td>Varies by distance, weather and service quality<\/td><\/tr>\n            <tr><td>Taxi \/ rideshare<\/td><td>Often rounded up or a small percentage<\/td><\/tr>\n            <tr><td>Hairdresser \/ salon<\/td><td>Commonly a percentage of the service cost<\/td><\/tr>\n            <tr><td>Hotel housekeeping<\/td><td>Often a small fixed amount per night<\/td><\/tr>\n            <tr><td>Bartender<\/td><td>Varies by drink or total bill<\/td><\/tr>\n            <tr><td>Valet parking<\/td><td>Often a small fixed amount<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>`;\n    }\n\n    calc();\n  }});\n\nreg({slug:'discount',name:'Discount Calculator',cat:'financial',icn:'ticket',photo:21,\n  desc:'A complete shopping savings toolkit: discounts with coupons\/tax\/cashback, unlimited sequential discounts, reverse calculators, 3-store deal comparison, and bulk purchase math.',\n  render(el){\n    const EXAMPLES=[25,50,100,250,500,1000];\n\n    el.innerHTML = `\n      <div class=\"dc-shell\">\n      <div class=\"seg\" id=\"dc-mode-seg\">\n        <button class=\"active\" data-v=\"single\">Single item<\/button>\n        <button data-v=\"multi\">Multiple discounts<\/button>\n        <button data-v=\"reverse\">Reverse calculators<\/button>\n        <button data-v=\"compare\">Compare 3 deals<\/button>\n        <button data-v=\"bulk\">Bulk purchase<\/button>\n      <\/div>\n\n      <div id=\"dc-single-fields\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Original price<\/label><input type=\"number\" id=\"dc-price\" value=\"120\"><\/div>\n          <div class=\"field\"><label>Discount (%)<\/label><input type=\"number\" id=\"dc-pct\" value=\"25\"><\/div>\n        <\/div>\n        <div style=\"margin:10px 0;\">${EXAMPLES.map(v=>`<span class=\"ln-example-chip\" data-v=\"${v}\">${money(v)}<\/span>`).join('')}<\/div>\n        <span class=\"more-link\" id=\"dc-more-link\">+ Coupon, tax, shipping &amp; cashback<\/span>\n        <div class=\"adv-panel more-panel-card\" id=\"dc-more-panel\">\n          <div class=\"form-row\">\n            <div class=\"field\"><label>Coupon (%, applied after discount)<\/label><input type=\"number\" id=\"dc-coupon\" value=\"0\"><\/div>\n            <div class=\"field\"><label>Sales tax (%)<\/label><input type=\"number\" id=\"dc-tax\" value=\"0\"><\/div>\n          <\/div>\n          <div class=\"seg\" id=\"dc-taxmode-seg\"><button class=\"active\" data-v=\"after\">Tax after discount<\/button><button data-v=\"before\">Tax before discount<\/button><\/div>\n          <div class=\"form-row\">\n            <div class=\"field\"><label>Shipping<\/label><input type=\"number\" id=\"dc-shipping\" value=\"0\"><\/div>\n            <div class=\"field\"><label>Cashback (%)<\/label><input type=\"number\" id=\"dc-cashback\" value=\"0\"><\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"dc-multi-fields\" hidden>\n        <div class=\"field\"><label>Original price<\/label><input type=\"number\" id=\"dc-multi-price\" value=\"200\"><\/div>\n        <div id=\"dc-stages\"><\/div>\n        <button class=\"btn btn-ghost btn-sm\" id=\"dc-stage-add\" type=\"button\" style=\"margin-bottom:12px;\">+ Add another discount<\/button>\n      <\/div>\n\n      <div id=\"dc-reverse-fields\" hidden>\n        <div class=\"field\"><label>Solve for<\/label><select id=\"dc-rev-mode\"><option value=\"pct\">Discount % (from original + sale price)<\/option><option value=\"orig\">Original price (from sale price + discount %)<\/option><\/select><\/div>\n        <div id=\"dc-rev-inputs\"><\/div>\n      <\/div>\n\n      <div id=\"dc-compare-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label><b>Store A<\/b> price<\/label><input type=\"number\" id=\"dc-cmp-a-price\" value=\"100\"><\/div>\n          <div class=\"field\"><label><b>Store B<\/b> price<\/label><input type=\"number\" id=\"dc-cmp-b-price\" value=\"110\"><\/div>\n          <div class=\"field\"><label><b>Store C<\/b> price<\/label><input type=\"number\" id=\"dc-cmp-c-price\" value=\"95\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>A discount %<\/label><input type=\"number\" id=\"dc-cmp-a-pct\" value=\"20\"><\/div>\n          <div class=\"field\"><label>B discount %<\/label><input type=\"number\" id=\"dc-cmp-b-pct\" value=\"25\"><\/div>\n          <div class=\"field\"><label>C discount %<\/label><input type=\"number\" id=\"dc-cmp-c-pct\" value=\"10\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>A shipping<\/label><input type=\"number\" id=\"dc-cmp-a-ship\" value=\"0\"><\/div>\n          <div class=\"field\"><label>B shipping<\/label><input type=\"number\" id=\"dc-cmp-b-ship\" value=\"5\"><\/div>\n          <div class=\"field\"><label>C shipping<\/label><input type=\"number\" id=\"dc-cmp-c-ship\" value=\"8\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"dc-bulk-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Unit price<\/label><input type=\"number\" id=\"dc-bulk-unit\" value=\"12\"><\/div>\n          <div class=\"field\"><label>Quantity<\/label><input type=\"number\" id=\"dc-bulk-qty\" value=\"50\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Discount (%)<\/label><input type=\"number\" id=\"dc-bulk-pct\" value=\"15\"><\/div>\n          <div class=\"field\"><label>Tax (%)<\/label><input type=\"number\" id=\"dc-bulk-tax\" value=\"0\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"dc-results\"><\/div>\n      <\/div>`;\n\n    let mode='single', taxMode='after';\n    $('dc-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('dc-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('dc-single-fields').hidden=mode!=='single';\n      $('dc-multi-fields').hidden=mode!=='multi';\n      $('dc-reverse-fields').hidden=mode!=='reverse';\n      $('dc-compare-fields').hidden=mode!=='compare';\n      $('dc-bulk-fields').hidden=mode!=='bulk';\n      calc();\n    });\n    $('dc-more-link').onclick=()=>$('dc-more-panel').classList.toggle('show');\n    $('dc-taxmode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{ $('dc-taxmode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); taxMode=b.dataset.v; calc(); });\n    el.querySelectorAll('.ln-example-chip').forEach(chip=>chip.onclick=()=>{ $('dc-price').value=chip.dataset.v; calc(); });\n    el.querySelectorAll('#dc-single-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#dc-multi-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#dc-compare-fields input').forEach(i=>i.addEventListener('input',calc));\n    el.querySelectorAll('#dc-bulk-fields input').forEach(i=>i.addEventListener('input',calc));\n\n    function addStage(val){\n      const row=document.createElement('div');\n      row.className='dc-stage-row';\n      row.innerHTML=`<input type=\"number\" class=\"dc-stage-pct\" placeholder=\"Discount %\" value=\"${val!==undefined?val:''}\"><button class=\"btn btn-ghost btn-sm\" type=\"button\" onclick=\"this.closest('.dc-stage-row').remove();window.dispatchEvent(new Event('dc-stage-change'))\">\u2715<\/button>`;\n      $('dc-stages').appendChild(row);\n      row.querySelector('input').addEventListener('input',calc);\n    }\n    addStage(30); addStage(10); addStage(5);\n    $('dc-stage-add').onclick=()=>addStage();\n    window.addEventListener('dc-stage-change',calc);\n\n    const REV_CONFIGS={\n      pct:[['dc-rev-orig','Original price',100],['dc-rev-sale','Sale price',75]],\n      orig:[['dc-rev-sale2','Sale price',75],['dc-rev-pct','Discount %',25]]\n    };\n    function renderRevInputs(){\n      const m=$('dc-rev-mode').value;\n      $('dc-rev-inputs').innerHTML = REV_CONFIGS[m].map(([id,label,def])=>`<div class=\"field\"><label>${label}<\/label><input type=\"number\" id=\"${id}\" value=\"${def}\"><\/div>`).join('');\n      REV_CONFIGS[m].forEach(([id])=>$(id).addEventListener('input',calc));\n      calc();\n    }\n    $('dc-rev-mode').onchange=renderRevInputs;\n\n    function calc(){\n      if(mode==='single') calcSingle();\n      else if(mode==='multi') calcMulti();\n      else if(mode==='reverse') calcReverse();\n      else if(mode==='compare') calcCompare();\n      else calcBulk();\n    }\n\n    function calcSingle(){\n      const price=+$('dc-price').value, pct=+$('dc-pct').value;\n      const coupon=+$('dc-coupon').value||0, tax=+$('dc-tax').value||0, shipping=+$('dc-shipping').value||0, cashback=+$('dc-cashback').value||0;\n      if(price<=0){ $('dc-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid original price.<\/div><\/div>'; return; }\n      const afterDiscount=price*(1-pct\/100);\n      const afterCoupon=afterDiscount*(1-coupon\/100);\n      let taxAmt, finalPrice;\n      if(taxMode==='after'){ taxAmt=afterCoupon*tax\/100; finalPrice=afterCoupon+taxAmt+shipping; }\n      else { taxAmt=price*tax\/100; finalPrice=afterCoupon+taxAmt+shipping; }\n      const cashbackAmt=finalPrice*cashback\/100;\n      const effectivePrice=finalPrice-cashbackAmt;\n      const totalSaved=price-afterCoupon;\n      const effectiveDiscountPct=(totalSaved\/price)*100;\n\n      $('dc-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(effectivePrice)}<\/div><div class=\"dd-main-sub\">${cashback>0?'Effective price after cashback':'Final price'}<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Savings breakdown<\/h5><\/div>\n          <div class=\"st-receipt\">\n            <div class=\"rline\"><span>Original price<\/span><span>${money(price)}<\/span><\/div>\n            <div class=\"rline\"><span>Discount (${pct}%)<\/span><span>\u2212${money(price-afterDiscount)}<\/span><\/div>\n            ${coupon>0?`<div class=\"rline\"><span>Coupon (${coupon}%)<\/span><span>\u2212${money(afterDiscount-afterCoupon)}<\/span><\/div>`:''}\n            ${tax>0?`<div class=\"rline\"><span>Tax (${tax}%)<\/span><span>+${money(taxAmt)}<\/span><\/div>`:''}\n            ${shipping>0?`<div class=\"rline\"><span>Shipping<\/span><span>+${money(shipping)}<\/span><\/div>`:''}\n            <div class=\"rline total\"><span>Checkout total<\/span><span>${money(finalPrice)}<\/span><\/div>\n            ${cashback>0?`<div class=\"rline\"><span>Cashback (${cashback}%)<\/span><span>\u2212${money(cashbackAmt)}<\/span><\/div><div class=\"rline total\"><span>Effective price<\/span><span>${money(effectivePrice)}<\/span><\/div>`:''}\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Price breakdown<\/h5><\/div>\n          <div id=\"dc-pie\"><\/div>\n          <div id=\"dc-legend\" style=\"margin-top:10px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Compare discount rates<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Discount<\/th><th>You save<\/th><th>Final price<\/th><\/tr><\/thead><tbody>\n            ${[10,20,30,40,50].map(p=>{ const f=price*(1-p\/100); return `<tr><td>${p}%<\/td><td>${money(price-f)}<\/td><td>${money(f)}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"dc-copy\" type=\"button\">Copy result<\/button>\n        <\/div>`;\n\n      renderPieChart('dc-pie',['Final price','Total savings'],[+afterCoupon.toFixed(2),+totalSaved.toFixed(2)],['#4C6FFF','#1E9E6B']);\n      $('dc-legend').innerHTML = `<div class=\"m-item\">${dotSpan('#4C6FFF')}Final price <b style=\"margin-left:auto;\">${money(afterCoupon)}<\/b><\/div><div class=\"m-item\">${dotSpan('#1E9E6B')}Total savings <b style=\"margin-left:auto;\">${money(totalSaved)} (${num(effectiveDiscountPct,1)}%)<\/b><\/div>`;\n      $('dc-copy').onclick=()=>{ const text=`Final price: ${money(effectivePrice)} (saved ${money(totalSaved)}, ${num(effectiveDiscountPct,1)}% off). Calculated on MAFtrix.`; const done=()=>{$('dc-copy').textContent='Copied!';setTimeout(()=>$('dc-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done(); };\n    }\n\n    function calcMulti(){\n      const price=+$('dc-multi-price').value;\n      const stages=[...$('dc-stages').children].map(row=>+row.querySelector('input').value).filter(v=>v>0);\n      if(price<=0||!stages.length){ $('dc-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid price and at least one discount.<\/div><\/div>'; return; }\n      let running=price, steps=[];\n      stages.forEach((pct,i)=>{ const next=running*(1-pct\/100); steps.push({pct,before:running,after:next}); running=next; });\n      const totalSaved=price-running;\n      const equivalentPct=(totalSaved\/price)*100;\n      const sumOfPcts=stages.reduce((a,b)=>a+b,0);\n\n      $('dc-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(running)}<\/div><div class=\"dd-main-sub\">Final price after ${stages.length} sequential discounts<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Stage<\/th><th>Discount<\/th><th>Before<\/th><th>After<\/th><\/tr><\/thead><tbody>\n            ${steps.map((s,i)=>`<tr><td>${i+1}<\/td><td>${s.pct}%<\/td><td>${money(s.before)}<\/td><td>${money(s.after)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Equivalent single discount<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Total saved<\/span><b>${money(totalSaved)}<\/b><\/div>\n            <div class=\"m-item\"><span>Equivalent single discount<\/span><b>${num(equivalentPct,2)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Sum of individual percentages<\/span><b>${num(sumOfPcts,0)}%<\/b><\/div>\n          <\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Notice the equivalent discount (${num(equivalentPct,2)}%) is ${sumOfPcts>equivalentPct?'less than':'equal to'} the sum of the individual percentages (${num(sumOfPcts,0)}%) \u2014 sequential discounts multiply, they don't add, because each one applies to an already-reduced price.<\/p>\n        <\/div>`;\n    }\n\n    function calcReverse(){\n      const m=$('dc-rev-mode').value;\n      if(m==='pct'){\n        const orig=+$('dc-rev-orig').value, sale=+$('dc-rev-sale').value;\n        if(orig<=0){ $('dc-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid original price.<\/div><\/div>'; return; }\n        const pct=((orig-sale)\/orig)*100;\n        $('dc-results').innerHTML = `\n          <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(pct,2)}%<\/div><div class=\"dd-main-sub\">Discount percentage<\/div><\/div>\n          <div class=\"calc-section section-blue\"><div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">Discount % = (${money(orig)} \u2212 ${money(sale)}) \u00f7 ${money(orig)} \u00d7 100 = ${num(pct,2)}%<\/p><\/div>`;\n      } else {\n        const sale=+$('dc-rev-sale2').value, pct=+$('dc-rev-pct').value;\n        if(pct>=100){ $('dc-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Discount percentage must be less than 100%.<\/div><\/div>'; return; }\n        const orig=sale\/(1-pct\/100);\n        $('dc-results').innerHTML = `\n          <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(orig)}<\/div><div class=\"dd-main-sub\">Original price<\/div><\/div>\n          <div class=\"calc-section section-blue\"><div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">Original price = ${money(sale)} \u00f7 (1 \u2212 ${num(pct\/100,2)}) = ${money(sale)} \u00f7 ${num(1-pct\/100,2)} = ${money(orig)}<\/p><\/div>`;\n      }\n    }\n\n    function calcCompare(){\n      const stores=['a','b','c'].map(k=>({\n        label:k.toUpperCase(),\n        price:+$(`dc-cmp-${k}-price`).value,\n        pct:+$(`dc-cmp-${k}-pct`).value,\n        ship:+$(`dc-cmp-${k}-ship`).value||0\n      }));\n      stores.forEach(s=>{ s.final = s.price*(1-s.pct\/100)+s.ship; });\n      const minFinal=Math.min(...stores.map(s=>s.final));\n      $('dc-results').innerHTML = `\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Deal comparison<\/h5><\/div>\n          <div class=\"inline-fields\" style=\"align-items:stretch;\">\n            ${stores.map(s=>`<div class=\"dc-compare-col${s.final===minFinal?' best':''}\" style=\"flex:1;min-width:140px;\">\n              <div style=\"font-weight:700;margin-bottom:8px;\">Store ${s.label}${s.final===minFinal?' \ud83c\udfc6':''}<\/div>\n              <div style=\"font-size:.85rem;color:var(--text-soft);\">List: ${money(s.price)}<br>Discount: ${s.pct}%<br>Shipping: ${money(s.ship)}<\/div>\n              <div style=\"font-size:1.3rem;font-weight:700;margin-top:8px;color:${s.final===minFinal?'#1E9E6B':'var(--ink)'};\">${money(s.final)}<\/div>\n            <\/div>`).join('')}\n          <\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:12px;\">Store ${stores.find(s=>s.final===minFinal).label} offers the best total price once discount and shipping are both factored in \u2014 the biggest advertised discount percentage isn't always the cheapest final cost.<\/p>\n        <\/div>`;\n    }\n\n    function calcBulk(){\n      const unit=+$('dc-bulk-unit').value, qty=+$('dc-bulk-qty').value, pct=+$('dc-bulk-pct').value, tax=+$('dc-bulk-tax').value||0;\n      if(unit<=0||qty<=0){ $('dc-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid unit price and quantity.<\/div><\/div>'; return; }\n      const listTotal=unit*qty;\n      const afterDiscount=listTotal*(1-pct\/100);\n      const taxAmt=afterDiscount*tax\/100;\n      const total=afterDiscount+taxAmt;\n      const avgPerUnit=total\/qty;\n      $('dc-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${money(total)}<\/div><div class=\"dd-main-sub\">Total cost for ${qty} units<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Bulk purchase summary<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>List total<\/span><b>${money(listTotal)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total savings<\/span><b>${money(listTotal-afterDiscount)}<\/b><\/div>\n            <div class=\"m-item\"><span>Tax<\/span><b>${money(taxAmt)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total cost<\/span><b>${money(total)}<\/b><\/div>\n            <div class=\"m-item\"><span>Average cost per unit<\/span><b>${money(avgPerUnit)}<\/b><\/div>\n          <\/div>\n        <\/div>`;\n    }\n\n    renderRevInputs();\n    calc();\n  }});\n\n\/* ---------- MATH ---------- *\/\nreg({slug:'percentage',name:'Percentage Calculator',cat:'math',icn:'percent',photo:22,\n  desc:'A complete percentage hub: 10 calculation modes covering basic percentages, increase\/decrease, difference, error, discount, markup, margin and tax \u2014 all with step-by-step solutions.',\n  render(el){\n    const MODES=[\n      ['of','What is X% of Y?'],\n      ['isWhat','X is what % of Y?'],\n      ['isPercentOf','X is Y% of what number?'],\n      ['change','% increase or decrease (old \u2192 new)'],\n      ['difference','% difference between two numbers'],\n      ['error','% error (experimental vs. actual)'],\n      ['discount','Discount calculator'],\n      ['markup','Markup calculator'],\n      ['margin','Profit margin calculator'],\n      ['tax','Tax calculator']\n    ];\n    const EXAMPLES=[\n      ['5% of 100','of',5,100],['10% of 250','of',10,250],['20% discount on 100','discount',100,20],\n      ['25% increase from 80','change',80,100],['50% off 60','discount',60,50],['75% of 800','of',75,800]\n    ];\n\n    el.innerHTML = `\n      <div class=\"pc-shell\">\n      <div class=\"field\"><label>Calculation type<\/label>\n        <select id=\"pc-mode\">${MODES.map(([k,l])=>`<option value=\"${k}\">${l}<\/option>`).join('')}<\/select>\n      <\/div>\n      <div id=\"pc-inputs\"><\/div>\n      <div class=\"pc-bar-wrap\" id=\"pc-bar-wrap\" hidden><div class=\"pc-bar-fill\" id=\"pc-bar-fill\"><\/div><\/div>\n\n      <div class=\"dd-main-result\" id=\"pc-main-result\"><\/div>\n      <div id=\"pc-formula\"><\/div>\n      <div id=\"pc-steps\"><\/div>\n      <div id=\"pc-context\"><\/div>\n\n      <div class=\"calc-section section-blue\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Quick examples<\/h5><\/div>\n        <div id=\"pc-examples\">${EXAMPLES.map((e,i)=>`<span class=\"pc-example-chip\" data-i=\"${i}\">${e[0]}<\/span>`).join('')}<\/div>\n      <\/div>\n\n      <div class=\"calc-section section-green\" id=\"pc-table-section\" hidden>\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Percentage breakdown table<\/h5><\/div>\n        <div id=\"pc-table\"><\/div>\n      <\/div>\n\n      <div class=\"calc-section section-navy\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n        <button class=\"btn btn-ghost btn-block\" id=\"pc-copy\" type=\"button\">Copy result<\/button>\n      <\/div>\n      <\/div>`;\n\n    const INPUT_CONFIGS={\n      of:[['pc-x','X (percent)',25],['pc-y','Y (number)',800]],\n      isWhat:[['pc-x','X',40],['pc-y','Y',160]],\n      isPercentOf:[['pc-x','X',40],['pc-y','Percent',20]],\n      change:[['pc-x','Old value',150],['pc-y','New value',180]],\n      difference:[['pc-x','Value A',80],['pc-y','Value B',100]],\n      error:[['pc-x','Experimental value',48],['pc-y','Actual value',50]],\n      discount:[['pc-x','Original price',250],['pc-y','Discount %',15]],\n      markup:[['pc-x','Cost',80],['pc-y','Markup %',25]],\n      margin:[['pc-x','Cost',80],['pc-y','Revenue',100]],\n      tax:[['pc-x','Price before tax',80],['pc-y','Tax %',8.25]]\n    };\n    function renderInputs(){\n      const mode=$('pc-mode').value;\n      const cfg=INPUT_CONFIGS[mode];\n      $('pc-inputs').innerHTML = `<div class=\"form-row\">${cfg.map(([id,label,def])=>`<div class=\"field\"><label>${label}<\/label><input type=\"number\" id=\"${id}\" value=\"${def}\"><\/div>`).join('')}<\/div>`;\n      cfg.forEach(([id])=>{ $(id).oninput=calc; });\n      calc();\n    }\n    $('pc-mode').onchange=renderInputs;\n\n    function calc(){\n      const mode=$('pc-mode').value;\n      const x=+$('pc-x').value||0, y=+$('pc-y').value||0;\n      let mainVal, mainSub, formula, steps, context='', barPct=null, showTable=false;\n\n      if(mode==='of'){\n        const r=x\/100*y;\n        mainVal=num(r); mainSub=`${x}% of ${y}`;\n        formula='Result = (Percent \u00f7 100) \u00d7 Y';\n        steps=`Step 1: ${x} \u00f7 100 = ${num(x\/100,4)}<br>Step 2: ${num(x\/100,4)} \u00d7 ${y} = ${num(r)}`;\n        context=`${x}% of ${y} is ${num(r)}.`;\n        barPct=Math.min(100,x); showTable=true;\n      } else if(mode==='isWhat'){\n        const r=y!==0?x\/y*100:0;\n        mainVal=num(r)+'%'; mainSub=`${x} as a percent of ${y}`;\n        formula='Result = (X \u00f7 Y) \u00d7 100';\n        steps=`Step 1: ${x} \u00f7 ${y} = ${num(x\/y,4)}<br>Step 2: ${num(x\/y,4)} \u00d7 100 = ${num(r)}%`;\n        context=`${x} is ${num(r)}% of ${y}.`;\n        barPct=Math.min(100,Math.abs(r));\n      } else if(mode==='isPercentOf'){\n        const r=y!==0?x\/(y\/100):0;\n        mainVal=num(r); mainSub=`${x} is ${y}% of this number`;\n        formula='Result = X \u00f7 (Percent \u00f7 100)';\n        steps=`Step 1: ${y} \u00f7 100 = ${num(y\/100,4)}<br>Step 2: ${x} \u00f7 ${num(y\/100,4)} = ${num(r)}`;\n        context=`${x} is ${y}% of ${num(r)}.`;\n      } else if(mode==='change'){\n        const chg=x!==0?((y-x)\/x*100):0;\n        const dir=chg>=0?'increase':'decrease';\n        mainVal=(chg>=0?'+':'')+num(chg)+'%'; mainSub=`${dir} from ${x} to ${y}`;\n        formula='% Change = (New \u2212 Old) \u00f7 Old \u00d7 100';\n        steps=`Step 1: ${y} \u2212 ${x} = ${num(y-x)}<br>Step 2: ${num(y-x)} \u00f7 ${x} = ${num((y-x)\/x,4)}<br>Step 3: \u00d7 100 = ${(chg>=0?'+':'')}${num(chg)}%`;\n        context=`Going from ${x} to ${y} is a ${num(Math.abs(chg),1)}% ${dir}.`;\n      } else if(mode==='difference'){\n        const avg=(x+y)\/2;\n        const diff=avg!==0?Math.abs(x-y)\/avg*100:0;\n        mainVal=num(diff)+'%'; mainSub=`% difference between ${x} and ${y}`;\n        formula='% Difference = |A \u2212 B| \u00f7 ((A + B) \u00f7 2) \u00d7 100';\n        steps=`Step 1: |${x} \u2212 ${y}| = ${num(Math.abs(x-y))}<br>Step 2: (${x} + ${y}) \u00f7 2 = ${num(avg)}<br>Step 3: ${num(Math.abs(x-y))} \u00f7 ${num(avg)} \u00d7 100 = ${num(diff)}%`;\n        context=`${x} and ${y} differ by ${num(diff)}% (symmetric difference, unlike percentage change).`;\n      } else if(mode==='error'){\n        const err=y!==0?Math.abs(x-y)\/Math.abs(y)*100:0;\n        mainVal=num(err)+'%'; mainSub='Percentage error';\n        formula='% Error = |Experimental \u2212 Actual| \u00f7 Actual \u00d7 100';\n        steps=`Step 1: |${x} \u2212 ${y}| = ${num(Math.abs(x-y))}<br>Step 2: ${num(Math.abs(x-y))} \u00f7 ${y} = ${num(Math.abs(x-y)\/y,4)}<br>Step 3: \u00d7 100 = ${num(err)}%`;\n        context=`Your experimental value differs from the actual value by ${num(err)}%.`;\n      } else if(mode==='discount'){\n        const savings=x*y\/100, final=x-savings;\n        mainVal=money(final); mainSub=`Final price after ${y}% discount`;\n        formula='Final Price = Original \u00d7 (1 \u2212 Discount% \u00f7 100)';\n        steps=`Step 1: ${x} \u00d7 ${y} \u00f7 100 = ${money(savings)} saved<br>Step 2: ${x} \u2212 ${money(savings)} = ${money(final)}`;\n        context=`A ${y}% discount on ${money(x)} saves you ${money(savings)}, for a final price of ${money(final)}.`;\n      } else if(mode==='markup'){\n        const price=x*(1+y\/100);\n        mainVal=money(price); mainSub=`Selling price with ${y}% markup`;\n        formula='Selling Price = Cost \u00d7 (1 + Markup% \u00f7 100)';\n        steps=`Step 1: ${y} \u00f7 100 = ${num(y\/100,4)}<br>Step 2: ${x} \u00d7 (1 + ${num(y\/100,4)}) = ${money(price)}`;\n        context=`A ${y}% markup on a ${money(x)} cost gives a selling price of ${money(price)}.`;\n      } else if(mode==='margin'){\n        const profit=y-x, margin=y!==0?profit\/y*100:0, markupPct=x!==0?profit\/x*100:0;\n        mainVal=num(margin,1)+'%'; mainSub='Gross profit margin';\n        formula='Margin = (Revenue \u2212 Cost) \u00f7 Revenue \u00d7 100';\n        steps=`Step 1: ${y} \u2212 ${x} = ${money(profit)} profit<br>Step 2: ${money(profit)} \u00f7 ${y} \u00d7 100 = ${num(margin,1)}%`;\n        context=`Cost ${money(x)}, revenue ${money(y)}: profit margin is ${num(margin,1)}%, equivalent to a ${num(markupPct,1)}% markup on cost.`;\n      } else if(mode==='tax'){\n        const taxAmt=x*y\/100, total=x+taxAmt;\n        mainVal=money(total); mainSub=`Total price with ${y}% tax`;\n        formula='Total = Price \u00d7 (1 + Tax% \u00f7 100)';\n        steps=`Step 1: ${x} \u00d7 ${y} \u00f7 100 = ${money(taxAmt)} tax<br>Step 2: ${x} + ${money(taxAmt)} = ${money(total)}`;\n        context=`A ${y}% tax on ${money(x)} adds ${money(taxAmt)}, for a total of ${money(total)}.`;\n      }\n\n      $('pc-main-result').innerHTML = `<div class=\"dd-main-value\">${mainVal}<\/div><div class=\"dd-main-sub\">${mainSub}<\/div>`;\n      $('pc-formula').innerHTML = `<div class=\"formula-box\"><div class=\"f-label\">Formula<\/div><div class=\"f-eq\">${formula}<\/div><\/div>`;\n      $('pc-steps').innerHTML = `<div class=\"example-box\"><div class=\"e-label\">Step-by-step<\/div><p style=\"margin:0;\">${steps}<\/p><\/div>`;\n      $('pc-context').innerHTML = `<div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;\">${context}<\/div>`;\n\n      if(barPct!==null){ $('pc-bar-wrap').hidden=false; $('pc-bar-fill').style.width=barPct+'%'; $('pc-bar-fill').textContent=num(barPct,0)+'%'; }\n      else { $('pc-bar-wrap').hidden=true; }\n\n      if(showTable){\n        $('pc-table-section').hidden=false;\n        const pcts=[5,10,15,20,25,33,50,75,100];\n        $('pc-table').innerHTML = `<table class=\"ref-table\"><thead><tr><th>Percentage<\/th><th>Value<\/th><\/tr><\/thead><tbody>${pcts.map(p=>`<tr><td>${p}%<\/td><td>${num(p\/100*y)}<\/td><\/tr>`).join('')}<\/tbody><\/table>`;\n      } else { $('pc-table-section').hidden=true; }\n    }\n\n    document.querySelectorAll('#pc-examples .pc-example-chip').forEach(chip=>{});\n    $('pc-examples').addEventListener('click',(e)=>{\n      const chip=e.target.closest('.pc-example-chip'); if(!chip) return;\n      const [,mode,xv,yv]=EXAMPLES[+chip.dataset.i];\n      $('pc-mode').value=mode; renderInputs();\n      $('pc-x').value=xv; $('pc-y').value=yv; calc();\n    });\n\n    $('pc-copy').onclick=()=>{\n      const text=$('pc-main-result').querySelector('.dd-main-value').textContent+' \u2014 '+$('pc-main-result').querySelector('.dd-main-sub').textContent;\n      const done=()=>{$('pc-copy').textContent='Copied!';setTimeout(()=>$('pc-copy').textContent='Copy result',1500);};\n      navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done();\n    };\n\n    renderInputs();\n  }});\n\nreg({slug:'fraction',name:'Fraction Calculator',cat:'math',icn:'equation',photo:23,\n  desc:'A complete fraction hub: add\/subtract\/multiply\/divide with mixed numbers, decimal\/percentage conversion, fraction comparison, and step-by-step solutions with visuals.',\n  render(el){\n    function gcd(a,b){a=Math.abs(a);b=Math.abs(b);while(b){[a,b]=[b,a%b];}return a||1;}\n    function simplify(n,d){ const g=gcd(n,d); let rn=n\/g, rd=d\/g; if(rd<0){rn=-rn;rd=-rd;} return [rn,rd]; }\n    function toMixed(n,d){\n      const whole=Math.trunc(n\/d), rem=Math.abs(n%d);\n      return rem===0 ? `${whole}` : (whole!==0 ? `${whole} ${rem}\/${Math.abs(d)}` : `${n}\/${d}`);\n    }\n    function pieSVG(n,d,size=140){\n      const slices=Math.min(24,d);\n      let paths='';\n      for(let i=0;i<slices;i++){\n        const a1=(i\/slices)*2*Math.PI-Math.PI\/2, a2=((i+1)\/slices)*2*Math.PI-Math.PI\/2;\n        const cx=size\/2,cy=size\/2,r=size\/2-4;\n        const x1=cx+r*Math.cos(a1),y1=cy+r*Math.sin(a1),x2=cx+r*Math.cos(a2),y2=cy+r*Math.sin(a2);\n        const fill = i<n ? 'var(--teal)' : '#fff';\n        paths+=`<path d=\"M${cx},${cy} L${x1.toFixed(1)},${y1.toFixed(1)} A${r},${r} 0 0 1 ${x2.toFixed(1)},${y2.toFixed(1)} Z\" fill=\"${fill}\" stroke=\"var(--line)\" stroke-width=\"1.5\"\/>`;\n      }\n      return `<svg viewBox=\"0 0 ${size} ${size}\" width=\"${size}\" height=\"${size}\">${paths}<\/svg>`;\n    }\n    function barSVG(n,d,w=260,h=44){\n      const slices=Math.min(20,d);\n      let rects='';\n      for(let i=0;i<slices;i++){\n        const x=(i\/slices)*w;\n        const fill = i<n ? 'var(--teal)' : '#fff';\n        rects+=`<rect x=\"${x.toFixed(1)}\" y=\"0\" width=\"${(w\/slices).toFixed(1)}\" height=\"${h}\" fill=\"${fill}\" stroke=\"var(--line)\" stroke-width=\"1.5\"\/>`;\n      }\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\">${rects}<\/svg>`;\n    }\n    function numberLineHTML(val,label){\n      const w=280, clamped=Math.max(0,Math.min(1,val));\n      const ticks=[0,0.25,0.5,0.75,1];\n      let html=`<div class=\"fr-numline\" style=\"width:${w}px;\"><div class=\"line\"><\/div>`;\n      ticks.forEach(t=>{ html+=`<div class=\"tick\" style=\"left:${t*w}px;\"><\/div><div class=\"ticklabel\" style=\"left:${t*w}px;\">${t}<\/div>`; });\n      html+=`<div class=\"marker\" style=\"left:${clamped*w}px;\" title=\"${label}\"><\/div><\/div>`;\n      return html;\n    }\n\n    el.innerHTML = `\n      <div class=\"fr-shell\">\n      <div class=\"seg\" id=\"fr-mode-seg\">\n        <button class=\"active\" data-v=\"operate\">Operate<\/button>\n        <button data-v=\"convert\">Convert<\/button>\n        <button data-v=\"compare\">Compare<\/button>\n      <\/div>\n\n      <div id=\"fr-operate-fields\">\n        <div class=\"inline-fields\">\n          <div class=\"field\"><label>Whole 1<\/label><input type=\"number\" id=\"fr-w1\" value=\"0\" style=\"max-width:70px;\"><\/div>\n          <div class=\"field\"><label>Num 1<\/label><input type=\"number\" id=\"fr-n1\" value=\"1\"><\/div>\n          <div class=\"field\"><label>Den 1<\/label><input type=\"number\" id=\"fr-d1\" value=\"2\"><\/div>\n          <div class=\"field\"><label>Op<\/label><select id=\"fr-op\"><option>+<\/option><option>\u2212<\/option><option>\u00d7<\/option><option>\u00f7<\/option><\/select><\/div>\n          <div class=\"field\"><label>Whole 2<\/label><input type=\"number\" id=\"fr-w2\" value=\"0\" style=\"max-width:70px;\"><\/div>\n          <div class=\"field\"><label>Num 2<\/label><input type=\"number\" id=\"fr-n2\" value=\"3\"><\/div>\n          <div class=\"field\"><label>Den 2<\/label><input type=\"number\" id=\"fr-d2\" value=\"4\"><\/div>\n        <\/div>\n      <\/div>\n      <div id=\"fr-convert-fields\" hidden>\n        <div class=\"inline-fields\">\n          <div class=\"field\"><label>Numerator<\/label><input type=\"number\" id=\"fr-cn\" value=\"3\"><\/div>\n          <div class=\"field\"><label>Denominator<\/label><input type=\"number\" id=\"fr-cd\" value=\"4\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>Or enter a decimal \/ percentage to convert to a fraction<\/label><input type=\"text\" id=\"fr-dec-input\" placeholder=\"e.g. 0.625 or 40%\"><\/div>\n      <\/div>\n      <div id=\"fr-compare-fields\" hidden>\n        <div class=\"inline-fields\">\n          <div class=\"field\"><label>Num A<\/label><input type=\"number\" id=\"fr-an\" value=\"3\"><\/div>\n          <div class=\"field\"><label>Den A<\/label><input type=\"number\" id=\"fr-ad\" value=\"4\"><\/div>\n          <div class=\"field\"><label>Num B<\/label><input type=\"number\" id=\"fr-bn\" value=\"5\"><\/div>\n          <div class=\"field\"><label>Den B<\/label><input type=\"number\" id=\"fr-bd\" value=\"8\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"fr-results\"><\/div>\n      <\/div>`;\n\n    let mode='operate';\n    $('fr-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('fr-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('fr-operate-fields').hidden=mode!=='operate';\n      $('fr-convert-fields').hidden=mode!=='convert';\n      $('fr-compare-fields').hidden=mode!=='compare';\n      calc();\n    });\n    el.querySelectorAll('#fr-operate-fields input,#fr-operate-fields select').forEach(i=>i.oninput=i.onchange=calc);\n    el.querySelectorAll('#fr-convert-fields input').forEach(i=>i.oninput=calc);\n    el.querySelectorAll('#fr-compare-fields input').forEach(i=>i.oninput=calc);\n\n    function calc(){\n      if(mode==='operate') calcOperate();\n      else if(mode==='convert') calcConvert();\n      else calcCompare();\n    }\n\n    function calcOperate(){\n      const w1=+$('fr-w1').value||0, n1raw=+$('fr-n1').value||0, d1=+$('fr-d1').value||1;\n      const w2=+$('fr-w2').value||0, n2raw=+$('fr-n2').value||0, d2=+$('fr-d2').value||1;\n      const op=$('fr-op').value;\n      if(!d1||!d2){ $('fr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Denominator cannot be zero.<\/div><\/div>'; return; }\n      const n1 = w1*d1+n1raw;\n      const n2 = w2*d2+n2raw;\n\n      let rn,rd,stepsHTML;\n      if(op==='+'||op==='\u2212'){\n        const lcd=(d1*d2)\/gcd(d1,d2);\n        const cn1=n1*(lcd\/d1), cn2=n2*(lcd\/d2);\n        rn = op==='+' ? cn1+cn2 : cn1-cn2; rd=lcd;\n        stepsHTML = `Step 1 \u2014 Find the LCD of ${d1} and ${d2}: <b>${lcd}<\/b><br>Step 2 \u2014 Convert: ${n1}\/${d1} = ${cn1}\/${lcd}, ${n2}\/${d2} = ${cn2}\/${lcd}<br>Step 3 \u2014 ${op==='+'?'Add':'Subtract'}: ${cn1} ${op} ${cn2} = ${rn}, giving ${rn}\/${rd}`;\n      } else if(op==='\u00d7'){\n        rn=n1*n2; rd=d1*d2;\n        stepsHTML = `Step 1 \u2014 Multiply numerators: ${n1} \u00d7 ${n2} = ${rn}<br>Step 2 \u2014 Multiply denominators: ${d1} \u00d7 ${d2} = ${rd}`;\n      } else {\n        rn=n1*d2; rd=d1*n2;\n        stepsHTML = `Step 1 \u2014 Flip the second fraction: ${n2}\/${d2} \u2192 ${d2}\/${n2}<br>Step 2 \u2014 Multiply: ${n1}\/${d1} \u00d7 ${d2}\/${n2} = ${rn}\/${rd}`;\n      }\n      const [sn,sd]=simplify(rn,rd);\n      const g=gcd(rn,rd);\n      const mixed=toMixed(sn,sd);\n\n      $('fr-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${sn}\/${sd}<\/div><div class=\"dd-main-sub\">${mixed!==`${sn}\/${sd}`?'= '+mixed+' (mixed number)':'Already in simplest form'}<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;line-height:1.8;\">${stepsHTML}${g>1?`<br>Step ${op==='+'||op==='\u2212'?'4':'3'} \u2014 Simplify: GCF of ${rn} and ${rd} is ${g}, giving ${sn}\/${sd}`:''}<\/p>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Visual &amp; conversions<\/h5><\/div>\n          <div class=\"fr-visual-row\">${sd<=24?pieSVG(Math.max(0,sn%sd===0?sd:sn),sd):'<p style=\"color:var(--text-soft);font-size:.85rem;\">Denominator too large to visualize.<\/p>'}<\/div>\n          <div class=\"summary-mini\"><div class=\"m-item\"><span>Decimal<\/span><b>${num(sn\/sd,6)}<\/b><\/div><div class=\"m-item\"><span>Percentage<\/span><b>${num(sn\/sd*100,2)}%<\/b><\/div><\/div>\n        <\/div>`;\n    }\n\n    function calcConvert(){\n      const decInput=$('fr-dec-input').value.trim();\n      let n=+$('fr-cn').value||0, d=+$('fr-cd').value||1;\n      let fromDecimal=false;\n      if(decInput){\n        fromDecimal=true;\n        let v = decInput.endsWith('%') ? parseFloat(decInput)\/100 : parseFloat(decInput);\n        if(!isNaN(v)){\n          const decStr=(decInput.endsWith('%')?parseFloat(decInput):v*1).toString();\n          const decPlaces = decInput.includes('.') ? (decInput.split('.')[1]||'').replace('%','').length : 0;\n          d=Math.pow(10,decPlaces||6); n=Math.round(v*d);\n        }\n      }\n      if(!d){ $('fr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Denominator cannot be zero.<\/div><\/div>'; return; }\n      const [sn,sd]=simplify(n,d);\n      const decimal=sn\/sd;\n      const isRepeating = !fromDecimal && (sd!==1) && hasRepeatingDecimal(sn,sd);\n\n      $('fr-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${fromDecimal?`${sn}\/${sd}`:num(decimal,6)}<\/div><div class=\"dd-main-sub\">${fromDecimal?`= ${decInput} as a fraction`:`${n}\/${d} as a decimal`}<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>All conversions<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            <tr><td>Simplified fraction<\/td><td>${sn}\/${sd}<\/td><\/tr>\n            <tr><td>Mixed number<\/td><td>${toMixed(sn,sd)}<\/td><\/tr>\n            <tr><td>Decimal<\/td><td>${num(decimal,6)}${isRepeating?' (repeating)':''}<\/td><\/tr>\n            <tr><td>Percentage<\/td><td>${num(decimal*100,2)}%<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Visual<\/h5><\/div>\n          <div class=\"fr-visual-row\">${Math.abs(sd)<=24?pieSVG(Math.abs(sn)%Math.abs(sd)===0?Math.abs(sd):Math.abs(sn),Math.abs(sd)):''}${barSVG(Math.abs(sn)%Math.abs(sd)===0?Math.abs(sd):Math.abs(sn),Math.abs(sd))}<\/div>\n          ${numberLineHTML(decimal,`${sn}\/${sd}`)}\n        <\/div>`;\n    }\n    function hasRepeatingDecimal(n,d){\n      d=Math.abs(d);\n      while(d%2===0) d\/=2;\n      while(d%5===0) d\/=5;\n      return d!==1;\n    }\n\n    function calcCompare(){\n      const an=+$('fr-an').value||0, ad=+$('fr-ad').value||1, bn=+$('fr-bn').value||0, bd=+$('fr-bd').value||1;\n      if(!ad||!bd){ $('fr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Denominator cannot be zero.<\/div><\/div>'; return; }\n      const da=an\/ad, db=bn\/bd;\n      const larger = da>db ? `${an}\/${ad}` : da<db ? `${bn}\/${bd}` : 'Equal';\n      $('fr-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${larger}<\/div><div class=\"dd-main-sub\">${da===db?`${an}\/${ad} equals ${bn}\/${bd}`:'is the larger fraction'}<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Side-by-side comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th><\/th><th>${an}\/${ad}<\/th><th>${bn}\/${bd}<\/th><\/tr><\/thead><tbody>\n            <tr><td>Decimal<\/td><td>${num(da,6)}<\/td><td>${num(db,6)}<\/td><\/tr>\n            <tr><td>Percentage<\/td><td>${num(da*100,2)}%<\/td><td>${num(db*100,2)}%<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Visual comparison<\/h5><\/div>\n          <div class=\"fr-visual-row\">\n            <div style=\"text-align:center;\"><p style=\"font-size:.8rem;color:var(--text-soft);\">${an}\/${ad}<\/p>${Math.abs(ad)<=24?pieSVG(Math.abs(an)%Math.abs(ad)===0?Math.abs(ad):Math.abs(an),Math.abs(ad),110):''}<\/div>\n            <div style=\"text-align:center;\"><p style=\"font-size:.8rem;color:var(--text-soft);\">${bn}\/${bd}<\/p>${Math.abs(bd)<=24?pieSVG(Math.abs(bn)%Math.abs(bd)===0?Math.abs(bd):Math.abs(bn),Math.abs(bd),110):''}<\/div>\n          <\/div>\n        <\/div>`;\n    }\n\n    calc();\n  }});\n\nreg({slug:'average',name:'Average Calculator',cat:'math',icn:'chart',photo:24,\n  desc:'A complete statistics hub: arithmetic mean, weighted average, geometric and harmonic mean, trimmed mean, full descriptive statistics, outlier detection and step-by-step solutions.',\n  render(el){\n    function parseNums(text){\n      return text.split(\/[,\\s\\n]+\/).map(s=>s.trim()).filter(s=>s!=='').map(Number).filter(n=>!isNaN(n));\n    }\n    function stats(nums){\n      const sorted=[...nums].sort((a,b)=>a-b);\n      const n=nums.length, sum=nums.reduce((a,b)=>a+b,0), mean=sum\/n;\n      const mid=Math.floor(n\/2), median = n%2 ? sorted[mid] : (sorted[mid-1]+sorted[mid])\/2;\n      const freq={}; nums.forEach(x=>freq[x]=(freq[x]||0)+1);\n      const maxFreq=Math.max(...Object.values(freq));\n      const modes = maxFreq>1 ? Object.keys(freq).filter(k=>freq[k]===maxFreq).map(Number) : [];\n      const variance = nums.reduce((a,x)=>a+(x-mean)**2,0)\/n;\n      const q1 = percentile(sorted,25), q3 = percentile(sorted,75);\n      const iqr=q3-q1;\n      const outliers = nums.filter(x=>x<q1-1.5*iqr || x>q3+1.5*iqr);\n      return {n,sum,mean,median,modes,min:sorted[0],max:sorted[n-1],range:sorted[n-1]-sorted[0],variance,sd:Math.sqrt(variance),q1,q3,iqr,outliers,sorted};\n    }\n    function percentile(sorted,p){\n      const idx=(p\/100)*(sorted.length-1);\n      const lo=Math.floor(idx), hi=Math.ceil(idx);\n      if(lo===hi) return sorted[lo];\n      return sorted[lo]+(sorted[hi]-sorted[lo])*(idx-lo);\n    }\n    function barChartHTML(nums, outliers, mean){\n      if(!nums.length) return '';\n      const max=Math.max(...nums,mean), min=Math.min(...nums,mean,0);\n      const range=max-min||1;\n      const meanPct=((mean-min)\/range)*100;\n      let html=`<div class=\"stat-bar-chart\"><div class=\"mean-line\" style=\"bottom:${meanPct}%;\" title=\"Mean\"><\/div>`;\n      nums.forEach((v,i)=>{\n        const h=((v-min)\/range)*140;\n        const isOut=outliers.includes(v);\n        html+=`<div class=\"bar-col\"><div class=\"bar${isOut?' outlier':''}\" style=\"height:${Math.max(2,h)}px;\" title=\"${v}\"><\/div><\/div>`;\n      });\n      html+='<\/div>';\n      return html;\n    }\n\n    el.innerHTML = `\n      <div class=\"av-shell\">\n      <div class=\"seg\" id=\"av-mode-seg\">\n        <button class=\"active\" data-v=\"simple\">Mean &amp; statistics<\/button>\n        <button data-v=\"weighted\">Weighted average<\/button>\n        <button data-v=\"geometric\">Geometric mean<\/button>\n        <button data-v=\"harmonic\">Harmonic mean<\/button>\n        <button data-v=\"trimmed\">Trimmed mean<\/button>\n      <\/div>\n\n      <div id=\"av-simple-fields\">\n        <div class=\"field\"><label>Numbers <small>(comma, space or newline separated)<\/small><\/label><textarea id=\"av-list\" rows=\"3\">12, 18, 22, 18, 30, 45, 25<\/textarea><\/div>\n      <\/div>\n      <div id=\"av-weighted-fields\" hidden>\n        <div id=\"av-wrows\"><\/div>\n        <button class=\"btn btn-ghost btn-sm\" id=\"av-wadd\" type=\"button\" style=\"margin-bottom:12px;\">+ Add value<\/button>\n      <\/div>\n      <div id=\"av-geo-fields\" hidden>\n        <div class=\"field\"><label>Numbers (positive values only)<\/label><textarea id=\"av-geo-list\" rows=\"3\">2, 8, 4, 16<\/textarea><\/div>\n      <\/div>\n      <div id=\"av-harm-fields\" hidden>\n        <div class=\"field\"><label>Numbers (rates\/speeds, non-zero)<\/label><textarea id=\"av-harm-list\" rows=\"3\">60, 40, 80<\/textarea><\/div>\n      <\/div>\n      <div id=\"av-trim-fields\" hidden>\n        <div class=\"field\"><label>Numbers<\/label><textarea id=\"av-trim-list\" rows=\"3\">5, 8, 9, 10, 11, 12, 50<\/textarea><\/div>\n        <div class=\"field\"><label>Trim from each end: <span id=\"av-trim-val\">1<\/span> value(s)<\/label><input type=\"range\" id=\"av-trim-n\" min=\"0\" max=\"3\" value=\"1\"><\/div>\n      <\/div>\n\n      <div id=\"av-results\"><\/div>\n      <\/div>`;\n\n    let mode='simple';\n    $('av-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('av-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('av-simple-fields').hidden=mode!=='simple';\n      $('av-weighted-fields').hidden=mode!=='weighted';\n      $('av-geo-fields').hidden=mode!=='geometric';\n      $('av-harm-fields').hidden=mode!=='harmonic';\n      $('av-trim-fields').hidden=mode!=='trimmed';\n      calc();\n    });\n    $('av-list').oninput=calc;\n    $('av-geo-list').oninput=calc;\n    $('av-harm-list').oninput=calc;\n    $('av-trim-list').oninput=calc;\n    $('av-trim-n').oninput=(e)=>{ $('av-trim-val').textContent=e.target.value; calc(); };\n\n    function addWRow(v,w){\n      const row=document.createElement('div');\n      row.className='row-add';\n      row.innerHTML=`<input type=\"number\" class=\"av-wv\" placeholder=\"Value\" value=\"${v!==undefined?v:''}\"><input type=\"number\" class=\"av-ww\" placeholder=\"Weight\" value=\"${w!==undefined?w:1}\" style=\"max-width:100px;\"><button class=\"btn btn-ghost btn-sm\" type=\"button\" onclick=\"this.parentElement.remove()\">\u2715<\/button>`;\n      $('av-wrows').appendChild(row);\n      row.querySelectorAll('input').forEach(i=>i.oninput=calc);\n    }\n    addWRow(85,40); addWRow(92,60);\n    $('av-wadd').onclick=()=>addWRow('',1);\n\n    function calc(){\n      if(mode==='simple') calcSimple();\n      else if(mode==='weighted') calcWeighted();\n      else if(mode==='geometric') calcGeometric();\n      else if(mode==='harmonic') calcHarmonic();\n      else calcTrimmed();\n    }\n\n    function calcSimple(){\n      const nums=parseNums($('av-list').value);\n      if(nums.length<1){ $('av-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Enter at least one number.<\/div><\/div>'; return; }\n      const s=stats(nums);\n      let insight;\n      if(Math.abs(s.mean-s.median)>s.sd*0.5 && s.sd>0) insight=`Your mean (${num(s.mean)}) and median (${num(s.median)}) differ noticeably, suggesting the data may be skewed or influenced by outliers.`;\n      else insight=`Your mean and median are close, suggesting a fairly symmetric dataset.`;\n      if(s.outliers.length) insight+=` ${s.outliers.length} potential outlier${s.outliers.length>1?'s':''} detected (highlighted in red below), which may be pulling the mean away from the median.`;\n\n      $('av-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(s.mean)}<\/div><div class=\"dd-main-sub\">Arithmetic mean of ${s.n} values<\/div><\/div>\n        <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;\">${insight}<\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Full statistics dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Mean<\/span><b>${num(s.mean)}<\/b><\/div>\n            <div class=\"m-item\"><span>Median<\/span><b>${num(s.median)}<\/b><\/div>\n            <div class=\"m-item\"><span>Mode<\/span><b>${s.modes.length?s.modes.join(', '):'None'}<\/b><\/div>\n            <div class=\"m-item\"><span>Minimum<\/span><b>${num(s.min)}<\/b><\/div>\n            <div class=\"m-item\"><span>Maximum<\/span><b>${num(s.max)}<\/b><\/div>\n            <div class=\"m-item\"><span>Range<\/span><b>${num(s.range)}<\/b><\/div>\n            <div class=\"m-item\"><span>Sum<\/span><b>${num(s.sum)}<\/b><\/div>\n            <div class=\"m-item\"><span>Count<\/span><b>${s.n}<\/b><\/div>\n            <div class=\"m-item\"><span>Std. deviation<\/span><b>${num(s.sd,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Variance<\/span><b>${num(s.variance,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Q1 \/ Q3<\/span><b>${num(s.q1)} \/ ${num(s.q3)}<\/b><\/div>\n            <div class=\"m-item\"><span>IQR<\/span><b>${num(s.iqr)}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step (mean)<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">Step 1 \u2014 Sum all values: ${nums.join(' + ')} = ${num(s.sum)}<br>Step 2 \u2014 Count the values: n = ${s.n}<br>Step 3 \u2014 Divide: ${num(s.sum)} \u00f7 ${s.n} = ${num(s.mean)}<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Data visualization<\/h5><\/div>\n          ${barChartHTML(nums,s.outliers,s.mean)}\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">Dashed line = mean. Red bars = potential outliers (outside 1.5\u00d7IQR from Q1\/Q3).<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Running average<\/h5><\/div>\n          <div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>#<\/th><th>Value<\/th><th>Running average<\/th><\/tr><\/thead><tbody>\n            ${nums.map((v,i)=>`<tr><td>${i+1}<\/td><td>${v}<\/td><td>${num(nums.slice(0,i+1).reduce((a,b)=>a+b,0)\/(i+1))}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table><\/div>\n        <\/div>`;\n    }\n\n    function calcWeighted(){\n      const rows=[...$('av-wrows').children];\n      const items=rows.map(r=>({v:+r.querySelector('.av-wv').value||0,w:+r.querySelector('.av-ww').value||0})).filter(x=>x.w>0);\n      if(!items.length){ $('av-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Add at least one value with a positive weight.<\/div><\/div>'; return; }\n      const totalW=items.reduce((a,x)=>a+x.w,0);\n      const weighted=items.reduce((a,x)=>a+x.v*x.w,0)\/totalW;\n      $('av-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(weighted)}<\/div><div class=\"dd-main-sub\">Weighted average<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">${items.map(x=>`(${x.v} \u00d7 ${x.w})`).join(' + ')} = ${num(items.reduce((a,x)=>a+x.v*x.w,0))}<br>Total weight: ${items.map(x=>x.w).join(' + ')} = ${totalW}<br>Weighted average = ${num(items.reduce((a,x)=>a+x.v*x.w,0))} \u00f7 ${totalW} = ${num(weighted)}<\/p>\n        <\/div>\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Weighted averages are the basis of GPA calculations \u2014 see the <a href=\"#\/calculator\/gpa\">GPA Calculator<\/a> for a dedicated tool.<\/p>`;\n    }\n\n    function calcGeometric(){\n      const nums=parseNums($('av-geo-list').value).filter(n=>n>0);\n      if(nums.length<1){ $('av-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Enter at least one positive number.<\/div><\/div>'; return; }\n      const product=nums.reduce((a,b)=>a*b,1);\n      const gm=Math.pow(product,1\/nums.length);\n      $('av-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(gm,4)}<\/div><div class=\"dd-main-sub\">Geometric mean of ${nums.length} values<\/div><\/div>\n        <div class=\"formula-box\"><div class=\"f-label\">Formula<\/div><div class=\"f-eq\">GM = \u207f\u221a(x\u2081 \u00d7 x\u2082 \u00d7 ... \u00d7 x\u2099)<\/div><\/div>\n        <p style=\"color:var(--text-soft);font-size:.9rem;\">Product: ${nums.join(' \u00d7 ')} = ${num(product,4)}<br>${nums.length}th root of ${num(product,4)} = ${num(gm,4)}<\/p>\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Geometric mean is commonly used for growth rates and investment returns, where values compound multiplicatively rather than add.<\/p>`;\n    }\n\n    function calcHarmonic(){\n      const nums=parseNums($('av-harm-list').value).filter(n=>n!==0);\n      if(nums.length<1){ $('av-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Enter at least one non-zero number.<\/div><\/div>'; return; }\n      const hm=nums.length\/nums.reduce((a,b)=>a+1\/b,0);\n      $('av-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(hm,4)}<\/div><div class=\"dd-main-sub\">Harmonic mean of ${nums.length} values<\/div><\/div>\n        <div class=\"formula-box\"><div class=\"f-label\">Formula<\/div><div class=\"f-eq\">HM = n \u00f7 (1\/x\u2081 + 1\/x\u2082 + ... + 1\/x\u2099)<\/div><\/div>\n        <p style=\"color:var(--text-soft);font-size:.9rem;\">Sum of reciprocals: ${nums.map(n=>'1\/'+n).join(' + ')} = ${num(nums.reduce((a,b)=>a+1\/b,0),6)}<br>${nums.length} \u00f7 ${num(nums.reduce((a,b)=>a+1\/b,0),6)} = ${num(hm,4)}<\/p>\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Harmonic mean is the correct average for rates like speed \u2014 e.g. averaging speeds over equal distances (not equal times).<\/p>`;\n    }\n\n    function calcTrimmed(){\n      const nums=parseNums($('av-trim-list').value);\n      const trimN=+$('av-trim-n').value;\n      if(nums.length<trimN*2+1){ $('av-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Not enough values for this trim amount.<\/div><\/div>'; return; }\n      const sorted=[...nums].sort((a,b)=>a-b);\n      const trimmed=sorted.slice(trimN, sorted.length-trimN);\n      const mean=trimmed.reduce((a,b)=>a+b,0)\/trimmed.length;\n      const untrimmedMean=nums.reduce((a,b)=>a+b,0)\/nums.length;\n      $('av-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(mean)}<\/div><div class=\"dd-main-sub\">Trimmed mean (removed ${trimN} from each end)<\/div><\/div>\n        <div class=\"summary-mini\"><div class=\"m-item\"><span>Untrimmed mean<\/span><b>${num(untrimmedMean)}<\/b><\/div><div class=\"m-item\"><span>Removed (low)<\/span><b>${sorted.slice(0,trimN).join(', ')||'none'}<\/b><\/div><div class=\"m-item\"><span>Removed (high)<\/span><b>${sorted.slice(sorted.length-trimN).join(', ')||'none'}<\/b><\/div><\/div>\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Trimmed means reduce the influence of extreme values \u2014 useful when a few outliers might otherwise skew a simple mean.<\/p>`;\n    }\n\n    calc();\n  }});\n\nreg({slug:'standard-deviation',name:'Standard Deviation Calculator',cat:'math',icn:'chart',photo:25,\n  desc:'A complete statistics analyzer: population and sample standard deviation, variance, quartiles, IQR, coefficient of variation, outlier detection, and a full step-by-step calculation table.',\n  render(el){\n    function parseNums(text){\n      return text.split(\/[,\\s\\n]+\/).map(s=>s.trim()).filter(s=>s!=='').map(Number).filter(n=>!isNaN(n));\n    }\n    function percentile(sorted,p){\n      const idx=(p\/100)*(sorted.length-1);\n      const lo=Math.floor(idx), hi=Math.ceil(idx);\n      if(lo===hi) return sorted[lo];\n      return sorted[lo]+(sorted[hi]-sorted[lo])*(idx-lo);\n    }\n\n    el.innerHTML = `\n      <div class=\"sd-shell\">\n      <div class=\"field\"><label>Numbers <small>(comma, space or newline separated)<\/small><\/label><textarea id=\"sd-list\" rows=\"3\">4, 8, 6, 5, 3, 9, 7, 12<\/textarea><\/div>\n      <div class=\"seg\" id=\"sd-type-seg\"><button class=\"active\" data-v=\"pop\">Population (\u03c3)<\/button><button data-v=\"sample\">Sample (s)<\/button><\/div>\n\n      <div id=\"sd-results\"><\/div>\n      <\/div>`;\n\n    let type='pop';\n    $('sd-type-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('sd-type-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); type=b.dataset.v; calc();\n    });\n    $('sd-list').oninput=calc;\n\n    function calc(){\n      const nums=parseNums($('sd-list').value);\n      if(nums.length<2){ $('sd-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Enter at least two numbers.<\/div><\/div>'; return; }\n\n      const n=nums.length, sum=nums.reduce((a,b)=>a+b,0), mean=sum\/n;\n      const sorted=[...nums].sort((a,b)=>a-b);\n      const mid=Math.floor(n\/2), median = n%2 ? sorted[mid] : (sorted[mid-1]+sorted[mid])\/2;\n      const freq={}; nums.forEach(x=>freq[x]=(freq[x]||0)+1);\n      const maxFreq=Math.max(...Object.values(freq));\n      const modes = maxFreq>1 ? Object.keys(freq).filter(k=>freq[k]===maxFreq).map(Number) : [];\n      const sqDiffs=nums.map(x=>(x-mean)**2);\n      const sumSq=sqDiffs.reduce((a,b)=>a+b,0);\n      const popVar=sumSq\/n, sampVar=n>1?sumSq\/(n-1):0;\n      const popSD=Math.sqrt(popVar), sampSD=Math.sqrt(sampVar);\n      const usedSD = type==='pop' ? popSD : sampSD;\n      const usedVar = type==='pop' ? popVar : sampVar;\n      const q1=percentile(sorted,25), q3=percentile(sorted,75), iqr=q3-q1;\n      const outliers=nums.filter(x=>x<q1-1.5*iqr || x>q3+1.5*iqr);\n      const cv = mean!==0 ? (usedSD\/Math.abs(mean))*100 : 0;\n\n      let spread;\n      if(cv<15) spread='tightly clustered around the mean';\n      else if(cv<30) spread='moderately spread around the mean';\n      else spread='highly variable relative to the mean';\n\n      $('sd-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(usedSD,4)}<\/div><div class=\"dd-main-sub\">${type==='pop'?'Population':'Sample'} standard deviation (n=${n})<\/div><\/div>\n\n        <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;\">Your data is ${spread} (coefficient of variation: ${num(cv,1)}%).${outliers.length?` ${outliers.length} potential outlier${outliers.length>1?'s':''} detected, which may be increasing the overall spread.`:''}<\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Full statistics dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Mean<\/span><b>${num(mean,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Median<\/span><b>${num(median)}<\/b><\/div>\n            <div class=\"m-item\"><span>Mode<\/span><b>${modes.length?modes.join(', '):'None'}<\/b><\/div>\n            <div class=\"m-item\"><span>Population SD (\u03c3)<\/span><b>${num(popSD,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Sample SD (s)<\/span><b>${num(sampSD,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Population variance<\/span><b>${num(popVar,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Sample variance<\/span><b>${num(sampVar,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Min \/ Max<\/span><b>${num(sorted[0])} \/ ${num(sorted[n-1])}<\/b><\/div>\n            <div class=\"m-item\"><span>Range<\/span><b>${num(sorted[n-1]-sorted[0])}<\/b><\/div>\n            <div class=\"m-item\"><span>Count (n)<\/span><b>${n}<\/b><\/div>\n            <div class=\"m-item\"><span>Q1 \/ Q3<\/span><b>${num(q1)} \/ ${num(q3)}<\/b><\/div>\n            <div class=\"m-item\"><span>IQR<\/span><b>${num(iqr)}<\/b><\/div>\n            <div class=\"m-item\"><span>Coefficient of variation<\/span><b>${num(cv,1)}%<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"formula-box\">\n          <div class=\"f-label\">Formula used (${type==='pop'?'population':'sample'})<\/div>\n          <div class=\"f-eq\">${type==='pop' ? '\u03c3 = \u221a( \u03a3(x \u2212 \u03bc)\u00b2 \u00f7 N )' : 's = \u221a( \u03a3(x \u2212 x\u0304)\u00b2 \u00f7 (n \u2212 1) )'}<\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step solution<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">\n            Step 1 \u2014 Calculate the mean: ${nums.join(' + ')} = ${num(sum)}, \u00f7 ${n} = ${num(mean,4)}<br>\n            Step 2 \u2014 Find each deviation from the mean (x \u2212 ${num(mean,4)})<br>\n            Step 3 \u2014 Square each deviation (see table below)<br>\n            Step 4 \u2014 Sum the squared deviations: ${num(sumSq,4)}<br>\n            Step 5 \u2014 Divide by ${type==='pop'?'N':'n \u2212 1'} = ${type==='pop'?n:n-1}: ${num(usedVar,4)}<br>\n            Step 6 \u2014 Take the square root: \u221a${num(usedVar,4)} = ${num(usedSD,4)}\n          <\/p>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Calculation table<\/h5><\/div>\n          <div class=\"schedule-wrap\"><table class=\"mini\"><thead><tr><th>x<\/th><th>x \u2212 mean<\/th><th>(x \u2212 mean)\u00b2<\/th><\/tr><\/thead><tbody>\n            ${nums.map(x=>`<tr><td>${x}<\/td><td>${num(x-mean,4)}<\/td><td>${num((x-mean)**2,4)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Data visualization<\/h5><\/div>\n          ${(()=>{ const max=Math.max(...nums,mean), min=Math.min(...nums,mean,0); const range=max-min||1; const meanPct=((mean-min)\/range)*100;\n            let html=`<div class=\"stat-bar-chart\"><div class=\"mean-line\" style=\"bottom:${meanPct}%;\"><\/div>`;\n            nums.forEach(v=>{ const h=((v-min)\/range)*140; const isOut=outliers.includes(v); html+=`<div class=\"bar-col\"><div class=\"bar${isOut?' outlier':''}\" style=\"height:${Math.max(2,h)}px;\" title=\"${v}\"><\/div><\/div>`; });\n            html+='<\/div>'; return html; })()}\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">Dashed line = mean. Red bars = potential outliers (IQR rule).<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>Box plot<\/h5><\/div>\n          ${(()=>{ const bmin=sorted[0], bmax=sorted[n-1], brange=bmax-bmin||1, w=280;\n            const xOf=v=>((v-bmin)\/brange)*w;\n            return `<div class=\"boxplot-wrap\" style=\"width:${w+20}px;\">\n              <div class=\"bp-line\" style=\"left:${xOf(bmin)}px;width:${xOf(q1)-xOf(bmin)}px;\"><\/div>\n              <div class=\"bp-line\" style=\"left:${xOf(q3)}px;width:${xOf(bmax)-xOf(q3)}px;\"><\/div>\n              <div class=\"bp-whisker\" style=\"left:${xOf(bmin)}px;\"><\/div>\n              <div class=\"bp-whisker\" style=\"left:${xOf(bmax)}px;\"><\/div>\n              <div class=\"bp-box\" style=\"left:${xOf(q1)}px;width:${Math.max(2,xOf(q3)-xOf(q1))}px;\"><\/div>\n              <div class=\"bp-median\" style=\"left:${xOf(median)}px;\"><\/div>\n              <div class=\"bp-label\" style=\"left:${xOf(bmin)}px;\">Min ${num(bmin)}<\/div>\n              <div class=\"bp-label\" style=\"left:${xOf(q1)}px;\">Q1<\/div>\n              <div class=\"bp-label\" style=\"left:${xOf(median)}px;\">Med<\/div>\n              <div class=\"bp-label\" style=\"left:${xOf(q3)}px;\">Q3<\/div>\n              <div class=\"bp-label\" style=\"left:${xOf(bmax)}px;\">Max ${num(bmax)}<\/div>\n            <\/div>`; })()}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; share<\/h5><\/div>\n          <button class=\"btn btn-ghost btn-block\" id=\"sd-copy\" type=\"button\">Copy result<\/button>\n        <\/div>`;\n\n      $('sd-copy').onclick=()=>{\n        const text=`${type==='pop'?'Population':'Sample'} standard deviation: ${num(usedSD,4)} (mean: ${num(mean,4)}, n=${n}). Calculated on MAFtrix.`;\n        const done=()=>{$('sd-copy').textContent='Copied!';setTimeout(()=>$('sd-copy').textContent='Copy result',1500);};\n        navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done();\n      };\n    }\n    calc();\n  }});\n\nreg({slug:'random-number',name:'Random Number Generator',cat:'math',icn:'dice',photo:26,\n  desc:'A complete random generator hub: integers with filters (unique, even\/odd, prime, multiples), decimals, dice\/coin\/lottery presets, name picker, team splitter, and a color generator.',\n  render(el){\n    function isPrime(n){\n      n=Math.round(n);\n      if(n<2) return false;\n      for(let i=2;i*i<=n;i++){ if(n%i===0) return false; }\n      return true;\n    }\n    function statsOf(nums){\n      const n=nums.length, sum=nums.reduce((a,b)=>a+b,0), mean=sum\/n;\n      const sorted=[...nums].sort((a,b)=>a-b);\n      const mid=Math.floor(n\/2), median=n%2?sorted[mid]:(sorted[mid-1]+sorted[mid])\/2;\n      const variance=nums.reduce((a,x)=>a+(x-mean)**2,0)\/n;\n      return {n,sum,mean,median,min:sorted[0],max:sorted[n-1],sd:Math.sqrt(variance)};\n    }\n\n    const PRESETS=[\n      ['\ud83c\udfb2','Dice (1-6)',()=>rollDice(6,1)],['\ud83e\ude99','Coin flip',()=>coinFlip()],\n      ['\ud83c\udfaf','Lottery (1-49)',()=>genList(1,49,6,true)],['\ud83c\udfab','Bingo (1-75)',()=>genList(1,75,1,true)],\n      ['\ud83c\udf40','Lucky number',()=>genList(1,100,1,false)],['\ud83d\udcc5','Random year',()=>genList(1980,2025,1,false)],\n      ['\ud83d\udcc6','Random day',()=>genList(1,31,1,false)],['\ud83d\uddd3','Random month',()=>genList(1,12,1,false)]\n    ];\n\n    el.innerHTML = `\n      <div class=\"rn-shell\">\n      <div class=\"seg\" id=\"rn-mode-seg\">\n        <button class=\"active\" data-v=\"integer\">Integers<\/button>\n        <button data-v=\"decimal\">Decimals<\/button>\n        <button data-v=\"presets\">Quick presets<\/button>\n        <button data-v=\"picker\">Name picker \/ shuffle \/ teams<\/button>\n        <button data-v=\"color\">Random color<\/button>\n      <\/div>\n\n      <div id=\"rn-integer-fields\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Minimum<\/label><input type=\"number\" id=\"rn-min\" value=\"1\"><\/div>\n          <div class=\"field\"><label>Maximum<\/label><input type=\"number\" id=\"rn-max\" value=\"100\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>How many numbers? <small>(max 200)<\/small><\/label><input type=\"number\" id=\"rn-count\" value=\"10\" min=\"1\" max=\"200\"><\/div>\n        <div class=\"inline-fields\" style=\"margin-bottom:10px;\">\n          <label class=\"adv-toggle\" style=\"margin:0;\"><input type=\"checkbox\" id=\"rn-unique\"> Unique only<\/label>\n          <label class=\"adv-toggle\" style=\"margin:0;\"><input type=\"checkbox\" id=\"rn-even\"> Even only<\/label>\n          <label class=\"adv-toggle\" style=\"margin:0;\"><input type=\"checkbox\" id=\"rn-odd\"> Odd only<\/label>\n          <label class=\"adv-toggle\" style=\"margin:0;\"><input type=\"checkbox\" id=\"rn-prime\"> Primes only<\/label>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Multiples of <small>(optional, 0 = off)<\/small><\/label><input type=\"number\" id=\"rn-mult\" value=\"0\"><\/div>\n          <div class=\"field\"><label>Sort order<\/label><select id=\"rn-sort\"><option value=\"random\">Random order<\/option><option value=\"asc\">Ascending<\/option><option value=\"desc\">Descending<\/option><\/select><\/div>\n        <\/div>\n        <button class=\"btn btn-primary btn-block\" id=\"rn-int-btn\" type=\"button\">Generate<\/button>\n      <\/div>\n\n      <div id=\"rn-decimal-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Minimum<\/label><input type=\"number\" id=\"rn-dmin\" value=\"0\"><\/div>\n          <div class=\"field\"><label>Maximum<\/label><input type=\"number\" id=\"rn-dmax\" value=\"100\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Decimal places<\/label><input type=\"number\" id=\"rn-dplaces\" value=\"2\" min=\"0\" max=\"10\"><\/div>\n          <div class=\"field\"><label>How many?<\/label><input type=\"number\" id=\"rn-dcount\" value=\"10\" min=\"1\" max=\"200\"><\/div>\n        <\/div>\n        <button class=\"btn btn-primary btn-block\" id=\"rn-dec-btn\" type=\"button\">Generate<\/button>\n      <\/div>\n\n      <div id=\"rn-presets-fields\" hidden>\n        <div class=\"rn-preset-grid\">${PRESETS.map((p,i)=>`<div class=\"rn-preset-btn\" data-i=\"${i}\"><span class=\"icn\">${p[0]}<\/span>${p[1]}<\/div>`).join('')}<\/div>\n        <div class=\"field\" style=\"margin-top:16px;\"><label>Custom dice: sides<\/label>\n          <div class=\"inline-fields\">\n            <select id=\"rn-dice-sides\"><option value=\"4\">D4<\/option><option value=\"6\" selected>D6<\/option><option value=\"8\">D8<\/option><option value=\"10\">D10<\/option><option value=\"12\">D12<\/option><option value=\"20\">D20<\/option><option value=\"100\">D100<\/option><\/select>\n            <input type=\"number\" id=\"rn-dice-count\" value=\"2\" min=\"1\" max=\"10\" style=\"max-width:90px;\">\n            <button class=\"btn btn-primary btn-sm\" id=\"rn-dice-btn\" type=\"button\">Roll<\/button>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"rn-picker-fields\" hidden>\n        <div class=\"field\"><label>Paste your list (one item per line)<\/label><textarea id=\"rn-list-input\" rows=\"6\">Alex\nSam\nJordan\nTaylor\nMorgan\nCasey<\/textarea><\/div>\n        <div class=\"form-row\">\n          <button class=\"btn btn-primary btn-block\" id=\"rn-pick-btn\" type=\"button\">Pick a winner<\/button>\n          <button class=\"btn btn-ghost btn-block\" id=\"rn-shuffle-btn\" type=\"button\">Shuffle list<\/button>\n        <\/div>\n        <div class=\"field\" style=\"margin-top:10px;\"><label>Split into teams<\/label>\n          <div class=\"inline-fields\">\n            <input type=\"number\" id=\"rn-team-count\" value=\"2\" min=\"2\" max=\"10\" style=\"max-width:90px;\">\n            <button class=\"btn btn-amber btn-sm\" id=\"rn-teams-btn\" type=\"button\">Split into teams<\/button>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div id=\"rn-color-fields\" hidden>\n        <button class=\"btn btn-primary btn-block\" id=\"rn-color-btn\" type=\"button\">Generate random color<\/button>\n      <\/div>\n\n      <div id=\"rn-results\"><\/div>\n      <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 All random numbers are generated in your browser using JavaScript's Math.random() \u2014 a pseudo-random source suitable for games, sampling and everyday use, but not for cryptographic or security purposes.<\/p>\n      <\/div>`;\n\n    let history=[];\n    function addHistory(label,result){\n      history.unshift({label,result,time:new Date().toLocaleTimeString()});\n      if(history.length>10) history.pop();\n      renderHistory();\n    }\n    function renderHistory(){\n      const box=$('rn-history');\n      if(!box) return;\n      box.innerHTML = history.length ? history.map(h=>`<div class=\"uc-recent-item\"><span>${h.label}<\/span><span>${h.result}<\/span><\/div>`).join('') : '<p style=\"color:var(--text-soft);font-size:.85rem;\">No history yet this session.<\/p>';\n    }\n\n    $('rn-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('rn-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      const mode=b.dataset.v;\n      $('rn-integer-fields').hidden=mode!=='integer';\n      $('rn-decimal-fields').hidden=mode!=='decimal';\n      $('rn-presets-fields').hidden=mode!=='presets';\n      $('rn-picker-fields').hidden=mode!=='picker';\n      $('rn-color-fields').hidden=mode!=='color';\n      $('rn-results').innerHTML='';\n    });\n\n    function renderResultsPanel(numbers,label){\n      const s=statsOf(numbers);\n      const dupCount = numbers.length - new Set(numbers).size;\n      $('rn-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\" style=\"font-size:1.3rem;word-break:break-word;\">${numbers.join(', ')}<\/div><div class=\"dd-main-sub\">${label}<\/div><\/div>\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Statistical summary<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Count<\/span><b>${s.n}<\/b><\/div>\n            <div class=\"m-item\"><span>Min<\/span><b>${num(s.min)}<\/b><\/div>\n            <div class=\"m-item\"><span>Max<\/span><b>${num(s.max)}<\/b><\/div>\n            <div class=\"m-item\"><span>Mean<\/span><b>${num(s.mean,2)}<\/b><\/div>\n            <div class=\"m-item\"><span>Median<\/span><b>${num(s.median,2)}<\/b><\/div>\n            <div class=\"m-item\"><span>Std. deviation<\/span><b>${num(s.sd,2)}<\/b><\/div>\n            <div class=\"m-item\"><span>Sum<\/span><b>${num(s.sum)}<\/b><\/div>\n            <div class=\"m-item\"><span>Duplicates<\/span><b>${dupCount}<\/b><\/div>\n          <\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">See the <a href=\"#\/calculator\/average\">Average Calculator<\/a> and <a href=\"#\/calculator\/standard-deviation\">Standard Deviation Calculator<\/a> for deeper analysis.<\/p>\n        <\/div>\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Copy &amp; export<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"rn-copy\" type=\"button\">Copy<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"rn-csv\" type=\"button\">Download CSV<\/button>\n          <\/div>\n        <\/div>\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Recent generations (this session)<\/h5><\/div>\n          <div id=\"rn-history\"><\/div>\n        <\/div>`;\n      $('rn-copy').onclick=()=>{ const done=()=>{$('rn-copy').textContent='Copied!';setTimeout(()=>$('rn-copy').textContent='Copy',1500);}; navigator.clipboard?navigator.clipboard.writeText(numbers.join(', ')).then(done).catch(done):done(); };\n      $('rn-csv').onclick=()=>{\n        const csv='Value\\n'+numbers.join('\\n');\n        const blob=new Blob([csv],{type:'text\/csv'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='random-numbers.csv'; a.click(); URL.revokeObjectURL(url);\n      };\n      renderHistory();\n    }\n\n    $('rn-int-btn').onclick=()=>{\n      const min=+$('rn-min').value, max=+$('rn-max').value, count=Math.min(200,Math.max(1,+$('rn-count').value));\n      const unique=$('rn-unique').checked, evenOnly=$('rn-even').checked, oddOnly=$('rn-odd').checked, primeOnly=$('rn-prime').checked, mult=+$('rn-mult').value||0, sortOrder=$('rn-sort').value;\n      if(max<=min){ $('rn-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Maximum must be greater than minimum.<\/div><\/div>'; return; }\n      let pool=[];\n      for(let i=Math.ceil(min);i<=Math.floor(max);i++){\n        if(evenOnly &#038;&#038; i%2!==0) continue;\n        if(oddOnly &#038;&#038; i%2===0) continue;\n        if(primeOnly &#038;&#038; !isPrime(i)) continue;\n        if(mult>0 && i%mult!==0) continue;\n        pool.push(i);\n      }\n      if(!pool.length){ $('rn-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">No matches<\/div><div class=\"r-value\">No numbers in this range satisfy your filters.<\/div><\/div>'; return; }\n      let result=[];\n      if(unique){\n        if(pool.length<count){ $('rn-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Not enough matching numbers for that many unique values.<\/div><\/div>'; return; }\n        const p=[...pool];\n        for(let i=0;i<count;i++){ const idx=Math.floor(Math.random()*p.length); result.push(p.splice(idx,1)[0]); }\n      } else {\n        for(let i=0;i<count;i++) result.push(pool[Math.floor(Math.random()*pool.length)]);\n      }\n      if(sortOrder==='asc') result.sort((a,b)=>a-b);\n      else if(sortOrder==='desc') result.sort((a,b)=>b-a);\n      renderResultsPanel(result, `${count} number(s) between ${min} and ${max}`);\n      addHistory(`Integers ${min}-${max} (n=${count})`, result.slice(0,5).join(',')+(result.length>5?'\u2026':''));\n    };\n\n    $('rn-dec-btn').onclick=()=>{\n      const min=+$('rn-dmin').value, max=+$('rn-dmax').value, places=Math.min(10,Math.max(0,+$('rn-dplaces').value)), count=Math.min(200,Math.max(1,+$('rn-dcount').value));\n      if(max<=min){ $('rn-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Maximum must be greater than minimum.<\/div><\/div>'; return; }\n      const result=[]; for(let i=0;i<count;i++) result.push(+(Math.random()*(max-min)+min).toFixed(places));\n      renderResultsPanel(result, `${count} decimal(s) between ${min} and ${max}`);\n      addHistory(`Decimals ${min}-${max} (n=${count})`, result.slice(0,5).join(',')+(result.length>5?'\u2026':''));\n    };\n\n    function rollDice(sides,count){\n      const rolls=[]; for(let i=0;i<count;i++) rolls.push(Math.floor(Math.random()*sides)+1);\n      $('rn-results').innerHTML = `<div class=\"dd-main-result\"><div class=\"dd-main-value\">${rolls.reduce((a,b)=>a+b,0)}<\/div><div class=\"dd-main-sub\">Total of ${count}\u00d7D${sides}<\/div><\/div><div style=\"text-align:center;margin-top:16px;\">${rolls.map(r=>`<span class=\"rn-die\">${r}<\/span>`).join('')}<\/div>`;\n      addHistory(`${count}\u00d7D${sides}`, rolls.join(', '));\n    }\n    function coinFlip(){\n      const result=Math.random()<0.5?'Heads':'Tails';\n      $('rn-results').innerHTML = `<div class=\"dd-main-result\"><div class=\"dd-main-value\">${result}<\/div><div class=\"dd-main-sub\">Coin flip result<\/div><\/div>`;\n      addHistory('Coin flip', result);\n    }\n    function genList(min,max,count,unique){\n      let pool=[]; for(let i=min;i<=max;i++) pool.push(i);\n      let result=[];\n      if(unique){ for(let i=0;i<count;i++){ const idx=Math.floor(Math.random()*pool.length); result.push(pool.splice(idx,1)[0]); } result.sort((a,b)=>a-b); }\n      else { for(let i=0;i<count;i++) result.push(pool[Math.floor(Math.random()*pool.length)]); }\n      renderResultsPanel(result, `${count} number(s) between ${min} and ${max}`);\n      addHistory(`${min}-${max} (n=${count})`, result.join(', '));\n    }\n\n    $('rn-presets-fields').querySelectorAll('.rn-preset-btn').forEach(btn=>btn.onclick=()=>{\n      PRESETS[+btn.dataset.i][2]();\n    });\n    $('rn-dice-btn').onclick=()=>rollDice(+$('rn-dice-sides').value, Math.min(10,+$('rn-dice-count').value||1));\n\n    $('rn-pick-btn').onclick=()=>{\n      const items=$('rn-list-input').value.split('\\n').map(s=>s.trim()).filter(Boolean);\n      if(!items.length){ $('rn-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Add at least one item.<\/div><\/div>'; return; }\n      const winner=items[Math.floor(Math.random()*items.length)];\n      $('rn-results').innerHTML = `<div class=\"dd-main-result\"><div class=\"dd-main-value\">${winner}<\/div><div class=\"dd-main-sub\">Randomly picked from ${items.length} entries<\/div><\/div>`;\n      addHistory('Name picker', winner);\n    };\n    $('rn-shuffle-btn').onclick=()=>{\n      const items=$('rn-list-input').value.split('\\n').map(s=>s.trim()).filter(Boolean);\n      if(!items.length){ $('rn-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Add at least one item.<\/div><\/div>'; return; }\n      const shuffled=[...items];\n      for(let i=shuffled.length-1;i>0;i--){ const j=Math.floor(Math.random()*(i+1)); [shuffled[i],shuffled[j]]=[shuffled[j],shuffled[i]]; }\n      $('rn-results').innerHTML = `<div class=\"calc-section section-blue\"><div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('dice',16)}<\/div><h5>Shuffled order<\/h5><\/div><ol style=\"color:var(--text);font-size:.9rem;line-height:1.9;\">${shuffled.map(x=>`<li>${x}<\/li>`).join('')}<\/ol><\/div>`;\n      addHistory('List shuffle', shuffled.slice(0,5).join(', ')+(shuffled.length>5?'\u2026':''));\n    };\n    $('rn-teams-btn').onclick=()=>{\n      const items=$('rn-list-input').value.split('\\n').map(s=>s.trim()).filter(Boolean);\n      const teamCount=Math.max(2,Math.min(10,+$('rn-team-count').value||2));\n      if(items.length<teamCount){ $('rn-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Not enough entries for that many teams.<\/div><\/div>'; return; }\n      const shuffled=[...items];\n      for(let i=shuffled.length-1;i>0;i--){ const j=Math.floor(Math.random()*(i+1)); [shuffled[i],shuffled[j]]=[shuffled[j],shuffled[i]]; }\n      const teams=Array.from({length:teamCount},()=>[]);\n      shuffled.forEach((item,i)=>teams[i%teamCount].push(item));\n      $('rn-results').innerHTML = `<div class=\"calc-section section-blue\"><div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('dice',16)}<\/div><h5>Teams<\/h5><\/div>${teams.map((t,i)=>`<p style=\"font-size:.9rem;\"><b>Team ${i+1}:<\/b> ${t.join(', ')}<\/p>`).join('')}<\/div>`;\n      addHistory('Team split', `${teamCount} teams`);\n    };\n\n    $('rn-color-btn').onclick=()=>{\n      const r=Math.floor(Math.random()*256), g=Math.floor(Math.random()*256), b=Math.floor(Math.random()*256);\n      const hex='#'+[r,g,b].map(x=>x.toString(16).padStart(2,'0')).join('');\n      const max=Math.max(r,g,b)\/255, min=Math.min(r,g,b)\/255, l=(max+min)\/2;\n      let h=0,s=0;\n      if(max!==min){ const d=max-min; s=l>0.5?d\/(2-max-min):d\/(max+min);\n        if(max===r\/255) h=((g\/255-b\/255)\/d + (g<b?6:0))*60; else if(max===g\/255) h=((b\/255-r\/255)\/d+2)*60; else h=((r\/255-g\/255)\/d+4)*60; }\n      $('rn-results').innerHTML = `\n        <div class=\"rn-color-swatch\" style=\"background:${hex};\"><\/div>\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${hex.toUpperCase()}<\/div><div class=\"dd-main-sub\">Random color<\/div><\/div>\n        <div class=\"summary-mini\" style=\"margin-top:14px;\">\n          <div class=\"m-item\"><span>HEX<\/span><b>${hex.toUpperCase()}<\/b><\/div>\n          <div class=\"m-item\"><span>RGB<\/span><b>rgb(${r}, ${g}, ${b})<\/b><\/div>\n          <div class=\"m-item\"><span>HSL<\/span><b>hsl(${num(h,0)}, ${num(s*100,0)}%, ${num(l*100,0)}%)<\/b><\/div>\n        <\/div>`;\n      addHistory('Random color', hex.toUpperCase());\n    };\n\n    $('rn-int-btn').click();\n  }});\n\nreg({slug:'triangle',name:'Triangle Calculator',cat:'math',icn:'triangle',photo:27,\n  desc:'A complete triangle geometry hub: solve by SSS, SAS, ASA, right-triangle, or coordinates \u2014 with full properties, classification, step-by-step solutions and a live diagram.',\n  render(el){\n    function toDeg(r){ return r*180\/Math.PI; }\n    function toRad(d){ return d*Math.PI\/180; }\n    function fullProperties(a,b,c){\n      const s=(a+b+c)\/2;\n      const areaSq=s*(s-a)*(s-b)*(s-c);\n      if(areaSq<=0) return null;\n      const area=Math.sqrt(areaSq);\n      const A=toDeg(Math.acos(Math.max(-1,Math.min(1,(b*b+c*c-a*a)\/(2*b*c)))));\n      const B=toDeg(Math.acos(Math.max(-1,Math.min(1,(a*a+c*c-b*b)\/(2*a*c)))));\n      const C=180-A-B;\n      const perimeter=a+b+c;\n      const inradius=area\/s;\n      const circumradius=(a*b*c)\/(4*area);\n      const hA=2*area\/a, hB=2*area\/b, hC=2*area\/c;\n      const mA=0.5*Math.sqrt(2*b*b+2*c*c-a*a);\n      const mB=0.5*Math.sqrt(2*a*a+2*c*c-b*b);\n      const mC=0.5*Math.sqrt(2*a*a+2*b*b-c*c);\n      const eps=1e-6;\n      let sideClass = (Math.abs(a-b)<eps &#038;&#038; Math.abs(b-c)<eps) ? 'Equilateral' : (Math.abs(a-b)<eps||Math.abs(b-c)<eps||Math.abs(a-c)<eps) ? 'Isosceles' : 'Scalene';\n      const maxAngle=Math.max(A,B,C);\n      let angleClass = Math.abs(maxAngle-90)<0.5 ? 'Right' : maxAngle>90 ? 'Obtuse' : 'Acute';\n      return {a,b,c,A,B,C,area,perimeter,inradius,circumradius,hA,hB,hC,mA,mB,mC,sideClass,angleClass,s};\n    }\n    function isValidTriangle(a,b,c){ return a>0&&b>0&&c>0 && a+b>c && a+c>b && b+c>a; }\n\n    function svgDiagram(a,b,c,P){\n      const scale = 220\/Math.max(a,b,c);\n      const cx=c*scale;\n      const angleB = toRad(P.B);\n      const px = a*scale*Math.cos(angleB), py = a*scale*Math.sin(angleB);\n      const pad=36, w=Math.max(cx+pad*2,280), h=py+pad*2+20;\n      const heightFootX = px;\n      return `<svg width=\"${w}\" height=\"${h}\" viewBox=\"0 0 ${w} ${h}\">\n        <polygon points=\"${pad},${py+pad} ${cx+pad},${py+pad} ${px+pad},${pad}\" fill=\"rgba(14,165,160,.12)\" stroke=\"#0EA5A0\" stroke-width=\"2\"\/>\n        <line x1=\"${px+pad}\" y1=\"${pad}\" x2=\"${heightFootX+pad}\" y2=\"${py+pad}\" stroke=\"#F5A623\" stroke-width=\"1.5\" stroke-dasharray=\"4 3\"\/>\n        <text x=\"${(pad+cx+pad)\/2}\" y=\"${py+pad+20}\" text-anchor=\"middle\" font-size=\"12\" fill=\"#5B6472\" font-family=\"IBM Plex Mono, monospace\">c=${num(c,2)}<\/text>\n        <text x=\"${(pad+px+pad)\/2-16}\" y=\"${(py+pad+pad)\/2}\" text-anchor=\"middle\" font-size=\"12\" fill=\"#5B6472\" font-family=\"IBM Plex Mono, monospace\">b=${num(b,2)}<\/text>\n        <text x=\"${(cx+pad+px+pad)\/2+16}\" y=\"${(py+pad+pad)\/2}\" text-anchor=\"middle\" font-size=\"12\" fill=\"#5B6472\" font-family=\"IBM Plex Mono, monospace\">a=${num(a,2)}<\/text>\n        <text x=\"${pad-6}\" y=\"${py+pad+4}\" text-anchor=\"end\" font-size=\"11\" fill=\"#0F1C2E\" font-weight=\"700\">A<\/text>\n        <text x=\"${cx+pad+6}\" y=\"${py+pad+4}\" text-anchor=\"start\" font-size=\"11\" fill=\"#0F1C2E\" font-weight=\"700\">B<\/text>\n        <text x=\"${px+pad}\" y=\"${pad-8}\" text-anchor=\"middle\" font-size=\"11\" fill=\"#0F1C2E\" font-weight=\"700\">C<\/text>\n      <\/svg>`;\n    }\n\n    el.innerHTML = `\n      <div class=\"tr-shell\">\n      <div class=\"seg\" id=\"tr-mode-seg\">\n        <button class=\"active\" data-v=\"sss\">SSS (3 sides)<\/button>\n        <button data-v=\"sas\">SAS<\/button>\n        <button data-v=\"asa\">ASA<\/button>\n        <button data-v=\"right\">Right triangle<\/button>\n        <button data-v=\"coords\">Coordinates<\/button>\n      <\/div>\n\n      <div id=\"tr-sss-fields\">\n        <div class=\"inline-fields\">\n          <div class=\"field\"><label>Side a<\/label><input type=\"number\" id=\"tr-a\" value=\"7\"><\/div>\n          <div class=\"field\"><label>Side b<\/label><input type=\"number\" id=\"tr-b\" value=\"8\"><\/div>\n          <div class=\"field\"><label>Side c<\/label><input type=\"number\" id=\"tr-c\" value=\"9\"><\/div>\n        <\/div>\n      <\/div>\n      <div id=\"tr-sas-fields\" hidden>\n        <div class=\"inline-fields\">\n          <div class=\"field\"><label>Side a<\/label><input type=\"number\" id=\"tr-sas-a\" value=\"7\"><\/div>\n          <div class=\"field\"><label>Included angle C (\u00b0)<\/label><input type=\"number\" id=\"tr-sas-C\" value=\"50\"><\/div>\n          <div class=\"field\"><label>Side b<\/label><input type=\"number\" id=\"tr-sas-b\" value=\"8\"><\/div>\n        <\/div>\n      <\/div>\n      <div id=\"tr-asa-fields\" hidden>\n        <div class=\"inline-fields\">\n          <div class=\"field\"><label>Angle A (\u00b0)<\/label><input type=\"number\" id=\"tr-asa-A\" value=\"50\"><\/div>\n          <div class=\"field\"><label>Included side c<\/label><input type=\"number\" id=\"tr-asa-c\" value=\"9\"><\/div>\n          <div class=\"field\"><label>Angle B (\u00b0)<\/label><input type=\"number\" id=\"tr-asa-B\" value=\"60\"><\/div>\n        <\/div>\n      <\/div>\n      <div id=\"tr-right-fields\" hidden>\n        <div class=\"seg\" id=\"tr-right-mode-seg\" style=\"margin-bottom:14px;\">\n          <button class=\"active\" data-v=\"legs\">Two legs<\/button>\n          <button data-v=\"leghyp\">Leg + hypotenuse<\/button>\n        <\/div>\n        <div class=\"inline-fields\">\n          <div class=\"field\" id=\"tr-right-f1\"><label>Leg a<\/label><input type=\"number\" id=\"tr-right-a\" value=\"3\"><\/div>\n          <div class=\"field\" id=\"tr-right-f2\"><label>Leg b<\/label><input type=\"number\" id=\"tr-right-b\" value=\"4\"><\/div>\n        <\/div>\n      <\/div>\n      <div id=\"tr-coords-fields\" hidden>\n        <div class=\"tr-coords-row\">\n          <div class=\"field\"><label>x1<\/label><input type=\"number\" id=\"tr-x1\" value=\"0\"><\/div>\n          <div class=\"field\"><label>y1<\/label><input type=\"number\" id=\"tr-y1\" value=\"0\"><\/div>\n          <div class=\"field\"><label>x2<\/label><input type=\"number\" id=\"tr-x2\" value=\"6\"><\/div>\n          <div class=\"field\"><label>y2<\/label><input type=\"number\" id=\"tr-y2\" value=\"0\"><\/div>\n          <div class=\"field\"><label>x3<\/label><input type=\"number\" id=\"tr-x3\" value=\"2\"><\/div>\n          <div class=\"field\"><label>y3<\/label><input type=\"number\" id=\"tr-y3\" value=\"5\"><\/div>\n        <\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"tr-btn\" type=\"button\">Solve triangle<\/button>\n      <div id=\"tr-results\"><\/div>\n      <\/div>`;\n\n    let mode='sss', rightMode='legs';\n    $('tr-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('tr-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('tr-sss-fields').hidden=mode!=='sss';\n      $('tr-sas-fields').hidden=mode!=='sas';\n      $('tr-asa-fields').hidden=mode!=='asa';\n      $('tr-right-fields').hidden=mode!=='right';\n      $('tr-coords-fields').hidden=mode!=='coords';\n    });\n    $('tr-right-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('tr-right-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      rightMode=b.dataset.v;\n      $('tr-right-f1').innerHTML = rightMode==='legs' ? '<label>Leg a<\/label><input type=\"number\" id=\"tr-right-a\" value=\"3\">' : '<label>Leg a<\/label><input type=\"number\" id=\"tr-right-a\" value=\"3\">';\n      $('tr-right-f2').innerHTML = rightMode==='legs' ? '<label>Leg b<\/label><input type=\"number\" id=\"tr-right-b\" value=\"4\">' : '<label>Hypotenuse c<\/label><input type=\"number\" id=\"tr-right-b\" value=\"5\">';\n    });\n\n    function renderFull(a,b,c,stepsHTML){\n      const P=fullProperties(a,b,c);\n      if(!P){ $('tr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Invalid triangle<\/div><div class=\"r-value\">These measurements do not form a valid triangle.<\/div><\/div>'; return; }\n      let insight = `This is a ${P.sideClass.toLowerCase()} ${P.angleClass.toLowerCase()} triangle`;\n      if(P.angleClass==='Right') insight += ', since one angle measures 90\u00b0.';\n      else if(P.angleClass==='Obtuse') insight += `, since angle ${['A','B','C'][[P.A,P.B,P.C].indexOf(Math.max(P.A,P.B,P.C))]} exceeds 90\u00b0.`;\n      else insight += ', since all angles are less than 90\u00b0.';\n\n      $('tr-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(P.area,2)}<\/div><div class=\"dd-main-sub\">Area \u00b7 ${P.sideClass} \/ ${P.angleClass} triangle<\/div><\/div>\n        <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;\">${insight}<\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('triangle',16)}<\/div><h5>Full properties dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Side a<\/span><b>${num(P.a,3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Side b<\/span><b>${num(P.b,3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Side c<\/span><b>${num(P.c,3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Angle A<\/span><b>${num(P.A,2)}\u00b0<\/b><\/div>\n            <div class=\"m-item\"><span>Angle B<\/span><b>${num(P.B,2)}\u00b0<\/b><\/div>\n            <div class=\"m-item\"><span>Angle C<\/span><b>${num(P.C,2)}\u00b0<\/b><\/div>\n            <div class=\"m-item\"><span>Perimeter<\/span><b>${num(P.perimeter,3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Area<\/span><b>${num(P.area,3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Inradius<\/span><b>${num(P.inradius,3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Circumradius<\/span><b>${num(P.circumradius,3)}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>Altitudes &amp; medians<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th><\/th><th>To side a<\/th><th>To side b<\/th><th>To side c<\/th><\/tr><\/thead><tbody>\n            <tr><td>Altitude<\/td><td>${num(P.hA,3)}<\/td><td>${num(P.hB,3)}<\/td><td>${num(P.hC,3)}<\/td><\/tr>\n            <tr><td>Median<\/td><td>${num(P.mA,3)}<\/td><td>${num(P.mB,3)}<\/td><td>${num(P.mC,3)}<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">${stepsHTML}<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('triangle',16)}<\/div><h5>Diagram<\/h5><\/div>\n          <div style=\"display:flex;justify-content:center;\">${svgDiagram(P.a,P.b,P.c,P)}<\/div>\n        <\/div>`;\n    }\n\n    $('tr-btn').onclick=()=>{\n      if(mode==='sss'){\n        const a=+$('tr-a').value,b=+$('tr-b').value,c=+$('tr-c').value;\n        if(!isValidTriangle(a,b,c)){ $('tr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Invalid triangle<\/div><div class=\"r-value\">These three sides violate the Triangle Inequality Theorem (each side must be shorter than the sum of the other two).<\/div><\/div>'; return; }\n        const s=(a+b+c)\/2;\n        const steps=`Step 1 \u2014 Verify Triangle Inequality: ${a}+${b}>${c}, ${a}+${c}>${b}, ${b}+${c}>${a} \u2713 all hold<br>Step 2 \u2014 Heron's formula: s = (${a}+${b}+${c})\u00f72 = ${num(s,3)}<br>Step 3 \u2014 Area = \u221a(s(s\u2212a)(s\u2212b)(s\u2212c)) = \u221a(${num(s,2)}\u00d7${num(s-a,2)}\u00d7${num(s-b,2)}\u00d7${num(s-c,2)})<br>Step 4 \u2014 Angles via Law of Cosines: A = cos\u207b\u00b9((b\u00b2+c\u00b2\u2212a\u00b2)\u00f72bc), similarly for B and C`;\n        renderFull(a,b,c,steps);\n      } else if(mode==='sas'){\n        const a=+$('tr-sas-a').value, C=+$('tr-sas-C').value, b=+$('tr-sas-b').value;\n        if(a<=0||b<=0||C<=0||C>=180){ $('tr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter valid positive sides and an angle between 0\u00b0 and 180\u00b0.<\/div><\/div>'; return; }\n        const c=Math.sqrt(a*a+b*b-2*a*b*Math.cos(toRad(C)));\n        const steps=`Step 1 \u2014 Law of Cosines: c\u00b2 = a\u00b2 + b\u00b2 \u2212 2ab\u00b7cos(C) = ${num(a*a,2)} + ${num(b*b,2)} \u2212 2(${a})(${b})cos(${C}\u00b0)<br>Step 2 \u2014 c = \u221a${num(a*a+b*b-2*a*b*Math.cos(toRad(C)),3)} = ${num(c,3)}<br>Step 3 \u2014 Remaining angles found via Law of Cosines using all three sides`;\n        renderFull(a,b,c,steps);\n      } else if(mode==='asa'){\n        const A=+$('tr-asa-A').value, c=+$('tr-asa-c').value, B=+$('tr-asa-B').value;\n        if(A<=0||B<=0||c<=0||A+B>=180){ $('tr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Angles must be positive and sum to less than 180\u00b0.<\/div><\/div>'; return; }\n        const C=180-A-B;\n        const a=c*Math.sin(toRad(A))\/Math.sin(toRad(C));\n        const b=c*Math.sin(toRad(B))\/Math.sin(toRad(C));\n        const steps=`Step 1 \u2014 Third angle: C = 180\u00b0 \u2212 ${A}\u00b0 \u2212 ${B}\u00b0 = ${num(C,2)}\u00b0<br>Step 2 \u2014 Law of Sines: a\u00f7sin(A) = c\u00f7sin(C) \u2192 a = ${c}\u00d7sin(${A}\u00b0)\u00f7sin(${num(C,2)}\u00b0) = ${num(a,3)}<br>Step 3 \u2014 Similarly, b = ${c}\u00d7sin(${B}\u00b0)\u00f7sin(${num(C,2)}\u00b0) = ${num(b,3)}`;\n        renderFull(a,b,c,steps);\n      } else if(mode==='right'){\n        const v1=+$('tr-right-a').value, v2=+$('tr-right-b').value;\n        if(v1<=0||v2<=0){ $('tr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter valid positive values.<\/div><\/div>'; return; }\n        let a,b,c,steps;\n        if(rightMode==='legs'){\n          a=v1; b=v2; c=Math.sqrt(a*a+b*b);\n          steps=`Step 1 \u2014 Pythagorean theorem: c\u00b2 = a\u00b2 + b\u00b2 = ${num(a*a,2)} + ${num(b*b,2)} = ${num(a*a+b*b,2)}<br>Step 2 \u2014 c = \u221a${num(a*a+b*b,2)} = ${num(c,3)}`;\n        } else {\n          a=v1; c=v2;\n          if(c<=a){ $('tr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Hypotenuse must be longer than the leg.<\/div><\/div>'; return; }\n          b=Math.sqrt(c*c-a*a);\n          steps=`Step 1 \u2014 Pythagorean theorem: b\u00b2 = c\u00b2 \u2212 a\u00b2 = ${num(c*c,2)} \u2212 ${num(a*a,2)} = ${num(c*c-a*a,2)}<br>Step 2 \u2014 b = \u221a${num(c*c-a*a,2)} = ${num(b,3)}`;\n        }\n        renderFull(a,b,c,steps);\n      } else {\n        const x1=+$('tr-x1').value,y1=+$('tr-y1').value,x2=+$('tr-x2').value,y2=+$('tr-y2').value,x3=+$('tr-x3').value,y3=+$('tr-y3').value;\n        const a=Math.hypot(x3-x2,y3-y2), b=Math.hypot(x3-x1,y3-y1), c=Math.hypot(x2-x1,y2-y1);\n        const shoelaceArea=Math.abs((x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2))\/2);\n        const centroid=[(x1+x2+x3)\/3,(y1+y2+y3)\/3];\n        if(!isValidTriangle(a,b,c)){ $('tr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Invalid triangle<\/div><div class=\"r-value\">These three points are collinear (or coincide) and do not form a triangle.<\/div><\/div>'; return; }\n        const steps=`Step 1 \u2014 Side lengths via distance formula: a=${num(a,3)}, b=${num(b,3)}, c=${num(c,3)}<br>Step 2 \u2014 Area via Shoelace formula: \u00bd|x\u2081(y\u2082\u2212y\u2083)+x\u2082(y\u2083\u2212y\u2081)+x\u2083(y\u2081\u2212y\u2082)| = ${num(shoelaceArea,3)}<br>Step 3 \u2014 Centroid: ((x\u2081+x\u2082+x\u2083)\u00f73, (y\u2081+y\u2082+y\u2083)\u00f73) = (${num(centroid[0],2)}, ${num(centroid[1],2)})`;\n        renderFull(a,b,c,steps+`<br><br><b>Centroid: (${num(centroid[0],2)}, ${num(centroid[1],2)})<\/b>`);\n      }\n    };\n    $('tr-btn').click();\n  }});\n\nreg({slug:'quadratic',name:'Quadratic Equation Calculator',cat:'math',icn:'equation',photo:28,\n  desc:'A complete quadratic equation hub: discriminant analysis, exact and decimal roots, factored and vertex forms, full parabola analysis, step-by-step solving, and an interactive graph.',\n  render(el){\n    function gcd(a,b){a=Math.abs(a);b=Math.abs(b);while(b){[a,b]=[b,a%b];}return a||1;}\n    function simplifyRadical(n){\n      if(n<0) return null;\n      let outside=1, inside=n;\n      for(let i=2;i*i<=inside;i++){ while(inside%(i*i)===0){ inside\/=(i*i); outside*=i; } }\n      return {outside,inside};\n    }\n    function svgParabola(a,b,c,vertexX,vertexY,roots){\n      const w=320,h=260,pad=30;\n      let xr = Math.max(4, Math.abs(vertexX)*0.6+3);\n      if(roots.real) xr=Math.max(xr, Math.abs(roots.x1-vertexX)*1.6+1, Math.abs(roots.x2-vertexX)*1.6+1);\n      const xmin=vertexX-xr, xmax=vertexX+xr;\n      const pts=[]; let ymin=Infinity, ymax=-Infinity;\n      for(let i=0;i<=60;i++){ const x=xmin+(xmax-xmin)*i\/60; const y=a*x*x+b*x+c; pts.push([x,y]); if(y<ymin)ymin=y; if(y>ymax)ymax=y; }\n      ymin=Math.min(ymin,vertexY,0); ymax=Math.max(ymax,vertexY,0);\n      const yr=(ymax-ymin)||1; ymin-=yr*0.1; ymax+=yr*0.1;\n      const xOf=x=>pad+((x-xmin)\/(xmax-xmin))*(w-2*pad);\n      const yOf=y=>h-pad-((y-ymin)\/(ymax-ymin))*(h-2*pad);\n      const pathD=pts.map((p,i)=>`${i===0?'M':'L'}${xOf(p[0]).toFixed(1)},${yOf(p[1]).toFixed(1)}`).join(' ');\n      const zeroX=xOf(0), zeroY=yOf(0);\n      let markers='';\n      if(zeroY>=pad&&zeroY<=h-pad) markers+=`<line x1=\"${pad}\" y1=\"${zeroY}\" x2=\"${w-pad}\" y2=\"${zeroY}\" stroke=\"#CBD5E1\" stroke-width=\"1\"\/>`;\n      if(zeroX>=pad&&zeroX<=w-pad) markers+=`<line x1=\"${zeroX}\" y1=\"${pad}\" x2=\"${zeroX}\" y2=\"${h-pad}\" stroke=\"#CBD5E1\" stroke-width=\"1\"\/>`;\n      markers+=`<circle cx=\"${xOf(vertexX)}\" cy=\"${yOf(vertexY)}\" r=\"4.5\" fill=\"var(--amber)\"\/>`;\n      if(roots.real){\n        markers+=`<circle cx=\"${xOf(roots.x1)}\" cy=\"${yOf(0)}\" r=\"4\" fill=\"var(--danger)\"\/>`;\n        if(roots.x1!==roots.x2) markers+=`<circle cx=\"${xOf(roots.x2)}\" cy=\"${yOf(0)}\" r=\"4\" fill=\"var(--danger)\"\/>`;\n      }\n      markers+=`<circle cx=\"${xOf(0)}\" cy=\"${yOf(c)}\" r=\"4\" fill=\"#4C6FFF\"\/>`;\n      markers+=`<line x1=\"${xOf(vertexX)}\" y1=\"${pad}\" x2=\"${xOf(vertexX)}\" y2=\"${h-pad}\" stroke=\"var(--amber)\" stroke-width=\"1\" stroke-dasharray=\"3 3\"\/>`;\n      return `<svg width=\"100%\" height=\"${h}\" viewBox=\"0 0 ${w} ${h}\"><path d=\"${pathD}\" fill=\"none\" stroke=\"var(--teal)\" stroke-width=\"2.5\"\/>${markers}<\/svg>`;\n    }\n\n    el.innerHTML = `\n      <div class=\"qd-shell\">\n      <div class=\"field\"><label>ax\u00b2 + bx + c = 0<\/label><\/div>\n      <div class=\"inline-fields\">\n        <div class=\"field\"><label>a<\/label><input type=\"number\" id=\"qd-a\" value=\"1\" step=\"any\"><\/div>\n        <div class=\"field\"><label>b<\/label><input type=\"number\" id=\"qd-b\" value=\"-3\" step=\"any\"><\/div>\n        <div class=\"field\"><label>c<\/label><input type=\"number\" id=\"qd-c\" value=\"2\" step=\"any\"><\/div>\n      <\/div>\n      <label class=\"adv-toggle\"><input type=\"checkbox\" id=\"qd-sliders-toggle\"> Explore with sliders<\/label>\n      <div class=\"adv-panel more-panel-card\" id=\"qd-sliders-panel\">\n        <div class=\"field\"><label>a: <span id=\"qd-a-val\">1<\/span><\/label><input type=\"range\" id=\"qd-a-slider\" min=\"-5\" max=\"5\" step=\"0.5\" value=\"1\"><\/div>\n        <div class=\"field\"><label>b: <span id=\"qd-b-val\">-3<\/span><\/label><input type=\"range\" id=\"qd-b-slider\" min=\"-10\" max=\"10\" step=\"0.5\" value=\"-3\"><\/div>\n        <div class=\"field\"><label>c: <span id=\"qd-c-val\">2<\/span><\/label><input type=\"range\" id=\"qd-c-slider\" min=\"-10\" max=\"10\" step=\"0.5\" value=\"2\"><\/div>\n      <\/div>\n      <button class=\"btn btn-primary btn-block\" id=\"qd-btn\" type=\"button\">Solve equation<\/button>\n      <div id=\"qd-results\"><\/div>\n      <\/div>`;\n\n    $('qd-sliders-toggle').onchange=(e)=>{\n      $('qd-sliders-panel').classList.toggle('show',e.target.checked);\n      if(e.target.checked){ $('qd-a-slider').value=$('qd-a').value; $('qd-b-slider').value=$('qd-b').value; $('qd-c-slider').value=$('qd-c').value; $('qd-a-val').textContent=$('qd-a').value; $('qd-b-val').textContent=$('qd-b').value; $('qd-c-val').textContent=$('qd-c').value; }\n    };\n    ['a','b','c'].forEach(k=>{\n      $(`qd-${k}-slider`).oninput=(e)=>{ $(`qd-${k}-val`).textContent=e.target.value; $(`qd-${k}`).value=e.target.value; solve(); };\n    });\n\n    function solve(){\n      const a=+$('qd-a').value, b=+$('qd-b').value, c=+$('qd-c').value;\n      if(a===0){ $('qd-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Not quadratic<\/div><div class=\"r-value\">Coefficient \"a\" cannot be 0 \u2014 this would be a linear equation instead.<\/div><\/div>'; return; }\n      const disc=b*b-4*a*c;\n      const vertexX=-b\/(2*a), vertexY=a*vertexX*vertexX+b*vertexX+c;\n      let roots={real:false}, rootType, rootsDisplay, exactForm='';\n\n      if(disc>0){\n        const sq=Math.sqrt(disc);\n        const x1=(-b+sq)\/(2*a), x2=(-b-sq)\/(2*a);\n        roots={real:true,x1,x2};\n        rootType='Two distinct real roots';\n        const rad=simplifyRadical(disc);\n        if(rad && rad.inside!==disc){\n          exactForm = `x = (${-b} \u00b1 ${rad.outside>1?rad.outside+'\u221a'+rad.inside:'\u221a'+rad.inside}) \u00f7 ${2*a}`;\n        } else if(Number.isInteger(disc) && Number.isInteger(Math.sqrt(disc))){\n          exactForm = `x = (${-b} \u00b1 ${Math.sqrt(disc)}) \u00f7 ${2*a} \u2192 exact integer\/rational roots`;\n        } else {\n          exactForm = `x = (${-b} \u00b1 \u221a${num(disc,4)}) \u00f7 ${2*a}`;\n        }\n        rootsDisplay = `x\u2081 = ${num(x1,6)}, x\u2082 = ${num(x2,6)}`;\n      } else if(disc===0){\n        const x1=-b\/(2*a);\n        roots={real:true,x1,x2:x1};\n        rootType='One repeated real root';\n        exactForm = `x = ${-b} \u00f7 ${2*a}`;\n        rootsDisplay = `x = ${num(x1,6)} (double root)`;\n      } else {\n        const re=-b\/(2*a), im=Math.sqrt(-disc)\/(2*a);\n        roots={real:false,re,im};\n        rootType='Two complex conjugate roots';\n        exactForm = `x = ${num(re,4)} \u00b1 ${num(im,4)}i`;\n        rootsDisplay = exactForm;\n      }\n\n      \/\/ factored form (only if real roots and reasonably clean)\n      let factoredForm='Not applicable (complex roots)';\n      if(roots.real){\n        const fmtRoot=r=>{ const rr=Math.round(r*1000)\/1000; return rr===0?'x':(rr>0?`x \u2212 ${num(rr,3)}`:`x + ${num(-rr,3)}`); };\n        factoredForm = a===1 ? `(${fmtRoot(roots.x1)})(${fmtRoot(roots.x2)})` : `${a}(${fmtRoot(roots.x1)})(${fmtRoot(roots.x2)})`;\n      }\n      const vertexForm = `${a}(x ${vertexX>=0?'\u2212':'+'} ${num(Math.abs(vertexX),4)})\u00b2 ${vertexY>=0?'+':'\u2212'} ${num(Math.abs(vertexY),4)}`;\n\n      let insight = `Since a ${a>0?'> 0':'< 0'}, the parabola opens ${a>0?'upward':'downward'}, so the vertex is a ${a>0?'minimum':'maximum'} point. `;\n      insight += disc>0 ? 'The discriminant is positive, so the equation has two distinct real roots \u2014 the graph crosses the x-axis at two points.' : disc===0 ? 'The discriminant is zero, so the equation has one repeated real root \u2014 the graph touches the x-axis at exactly one point (the vertex).' : 'The discriminant is negative, so the equation has no real roots \u2014 the graph never crosses the x-axis.';\n\n      \/\/ verification\n      let verification='';\n      if(roots.real){\n        const check1=a*roots.x1*roots.x1+b*roots.x1+c;\n        const check2=a*roots.x2*roots.x2+b*roots.x2+c;\n        verification = `Check x\u2081: ${a}(${num(roots.x1,4)})\u00b2 + ${b}(${num(roots.x1,4)}) + ${c} = ${num(check1,6)} \u2248 0 \u2713<br>Check x\u2082: ${a}(${num(roots.x2,4)})\u00b2 + ${b}(${num(roots.x2,4)}) + ${c} = ${num(check2,6)} \u2248 0 \u2713`;\n      }\n\n      $('qd-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\" style=\"font-size:1.3rem;\">${rootsDisplay}<\/div><div class=\"dd-main-sub\">${rootType}<\/div><\/div>\n        <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;\">${insight}<\/div>\n\n        <div class=\"formula-box\"><div class=\"f-label\">Quadratic formula<\/div><div class=\"f-eq\">x = ( \u2212b \u00b1 \u221a(b\u00b2 \u2212 4ac) ) \u00f7 2a<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Step-by-step solution<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.9rem;\">\n            Step 1 \u2014 Identify: a=${a}, b=${b}, c=${c}<br>\n            Step 2 \u2014 Discriminant: D = b\u00b2 \u2212 4ac = (${b})\u00b2 \u2212 4(${a})(${c}) = ${num(disc,4)}<br>\n            Step 3 \u2014 Apply the formula: x = (${-b} \u00b1 \u221a${num(disc,4)}) \u00f7 ${2*a}<br>\n            Step 4 \u2014 Exact form: ${exactForm}<br>\n            Step 5 \u2014 Decimal form: ${rootsDisplay}\n          <\/p>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Equation analysis<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Discriminant<\/span><b>${num(disc,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>Vertex<\/span><b>(${num(vertexX,4)}, ${num(vertexY,4)})<\/b><\/div>\n            <div class=\"m-item\"><span>Axis of symmetry<\/span><b>x = ${num(vertexX,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>${a>0?'Minimum':'Maximum'} value<\/span><b>${num(vertexY,4)}<\/b><\/div>\n            <div class=\"m-item\"><span>y-intercept<\/span><b>(0, ${num(c,4)})<\/b><\/div>\n            <div class=\"m-item\"><span>Concavity<\/span><b>${a>0?'Opens upward':'Opens downward'}<\/b><\/div>\n            <div class=\"m-item\"><span>Domain<\/span><b>All real numbers<\/b><\/div>\n            <div class=\"m-item\"><span>Range<\/span><b>${a>0?`y \u2265 ${num(vertexY,3)}`:`y \u2264 ${num(vertexY,3)}`}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Alternate forms<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            <tr><td>Standard form<\/td><td>${a}x\u00b2 ${b>=0?'+':'\u2212'} ${num(Math.abs(b),3)}x ${c>=0?'+':'\u2212'} ${num(Math.abs(c),3)} = 0<\/td><\/tr>\n            <tr><td>Factored form<\/td><td>${factoredForm}<\/td><\/tr>\n            <tr><td>Vertex form<\/td><td>y = ${vertexForm}<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n\n        ${verification?`<div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Root verification<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">${verification}<\/p>\n        <\/div>`:''}\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Graph of the parabola<\/h5><\/div>\n          ${svgParabola(a,b,c,vertexX,vertexY,roots)}\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">Amber dot = vertex. Red dots = real roots (x-intercepts). Blue dot = y-intercept.<\/p>\n        <\/div>`;\n    }\n    ['qd-a','qd-b','qd-c'].forEach(id=>$(id).oninput=solve);\n    solve();\n  }});\n\nreg({slug:'scientific',name:'Scientific Calculator',cat:'math',icn:'calc',photo:29,\n  desc:'A full scientific calculator platform with trig, logs, factorials, combinatorics, memory functions, calculation history, angle mode switching and constants.',\n  render(el){\n    let angleMode='deg', memory=0, lastVal=null, history=[];\n\n    function factorial(n){ if(n<0||!Number.isInteger(n)) return NaN; if(n>170) return Infinity; let r=1; for(let i=2;i<=n;i++) r*=i; return r; }\n    function nPr(n,r){ if(r>n||r<0) return NaN; return factorial(n)\/factorial(n-r); }\n    function nCr(n,r){ if(r>n||r<0) return NaN; return factorial(n)\/(factorial(r)*factorial(n-r)); }\n\n    function evaluate(exprRaw){\n      \/\/ auto-balance any unclosed parentheses for a friendlier button-driven UX\n      let expr=exprRaw;\n      const opens=(expr.match(\/\\(\/g)||[]).length, closes=(expr.match(\/\\)\/g)||[]).length;\n      if(opens>closes) expr += ')'.repeat(opens-closes);\n      if(!\/^[0-9+\\-\u00d7\u00f7\u2212*\/^().,a-z!\\s]*$\/i.test(expr)) throw new Error('invalid characters');\n      const outFactor = angleMode==='deg' ? (180\/Math.PI) : angleMode==='grad' ? (200\/Math.PI) : 1;\n      const angFactor = angleMode==='deg' ? 'Math.PI\/180*' : angleMode==='grad' ? 'Math.PI\/200*' : '';\n      let js = expr\n        .replace(\/(\\d+)!\/g,'FACT($1)')\n        .replace(\/nPr\\(([^,]+),([^)]+)\\)\/g,'NPR($1,$2)')\n        .replace(\/nCr\\(([^,]+),([^)]+)\\)\/g,'NCR($1,$2)')\n        .replace(\/nthroot\\(([^,]+),([^)]+)\\)\/g,'NTHROOT($1,$2)')\n        .replace(\/mod\\(([^,]+),([^)]+)\\)\/g,'(($1)%($2))')\n        .replace(\/cbrt\\(\/g,'Math.cbrt(')\n        .replace(\/sqrt\\(\/g,'Math.sqrt(')\n        .replace(\/asin\\(\/g,'ASIN(')\n        .replace(\/acos\\(\/g,'ACOS(')\n        .replace(\/atan\\(\/g,'ATAN(')\n        .replace(\/sinh\\(\/g,'Math.sinh(')\n        .replace(\/cosh\\(\/g,'Math.cosh(')\n        .replace(\/tanh\\(\/g,'Math.tanh(')\n        .replace(\/sin\\(\/g,`Math.sin(${angFactor}`)\n        .replace(\/cos\\(\/g,`Math.cos(${angFactor}`)\n        .replace(\/tan\\(\/g,`Math.tan(${angFactor}`)\n        .replace(\/log2\\(\/g,'Math.log2(')\n        .replace(\/log\\(\/g,'Math.log10(')\n        .replace(\/ln\\(\/g,'Math.log(')\n        .replace(\/abs\\(\/g,'Math.abs(')\n        .replace(\/phi\/g,'1.6180339887')\n        .replace(\/pi\/g,'Math.PI')\n        .replace(\/\\be\\b\/g,'Math.E')\n        .replace(\/\\^\/g,'**')\n        .replace(\/\u00d7\/g,'*').replace(\/\u00f7\/g,'\/').replace(\/\u2212\/g,'-');\n      const FACT=factorial, NPR=nPr, NCR=nCr;\n      const ASIN=x=>Math.asin(x)*outFactor, ACOS=x=>Math.acos(x)*outFactor, ATAN=x=>Math.atan(x)*outFactor;\n      const NTHROOT=(x,n)=>Math.pow(x,1\/n);\n      \/\/ eslint-disable-next-line no-new-func\n      const result = Function('FACT','NPR','NCR','ASIN','ACOS','ATAN','NTHROOT','\"use strict\"; return (' + js + ')')(FACT,NPR,NCR,ASIN,ACOS,ATAN,NTHROOT);\n      if(typeof result !== 'number' || !isFinite(result)) throw new Error('invalid result');\n      return result;\n    }\n    function suggestFix(expr){\n      const typos={'sni(':'sin(','cso(':'cos(','tna(':'tan(','sqtr(':'sqrt(','lgo(':'log('};\n      for(const [wrong,right] of Object.entries(typos)){ if(expr.includes(wrong)) return `Did you mean \"${right}\" instead of \"${wrong}\"?`; }\n      return null;\n    }\n\n    \/\/ ---- button grid definition (matches classic handheld scientific calculator layout) ----\n    const ROWS=[\n      [['sin','fn'],['cos','fn'],['tan','fn'],['__ANGLE__','angle']],\n      [['sin\u207b\u00b9','fn'],['cos\u207b\u00b9','fn'],['tan\u207b\u00b9','fn'],['\u03c0','fn'],['e','fn']],\n      [['x\u02b8','fn'],['x\u00b3','fn'],['x\u00b2','fn'],['e\u02e3','fn'],['10\u02e3','fn']],\n      [['\u02b8\u221ax','fn'],['\u00b3\u221ax','fn'],['\u221ax','fn'],['ln','fn'],['log','fn']],\n      [['(','fn'],[')','fn'],['1\/x','fn'],['%','fn'],['n!','fn']],\n      [['7','num'],['8','num'],['9','num'],['+','op'],['Back','util']],\n      [['4','num'],['5','num'],['6','num'],['\u2212','op'],['Ans','util']],\n      [['1','num'],['2','num'],['3','num'],['\u00d7','op'],['M+','util']],\n      [['0','num'],['.','num'],['EXP','num'],['\u00f7','op'],['M\u2212','util']],\n      [['\u00b1','util2'],['RND','util2'],['AC','util2'],['=','util2'],['MR','util2']]\n    ];\n    function cellHTML([label,type]){\n      if(type==='angle'){\n        return `<div class=\"sc-angle-cell\">\n          <label><input type=\"radio\" name=\"sc-angle\" value=\"deg\" checked> Deg<\/label>\n          <label><input type=\"radio\" name=\"sc-angle\" value=\"rad\"> Rad<\/label>\n        <\/div>`;\n      }\n      const cls = label==='=' ? 'sc-eq' : (type==='op'||type==='util'||type==='util2') ? 'sc-op' : '';\n      return `<button type=\"button\" class=\"${cls}\" data-btn=\"${label}\">${label}<\/button>`;\n    }\n\n    el.innerHTML = `\n      <div class=\"sc-shell\">\n      <div class=\"sc-display\"><input type=\"text\" id=\"sc-expr\" value=\"\" placeholder=\"0\"><\/div>\n      <div class=\"sc-grid\" id=\"sc-grid\">\n        ${ROWS.map(row=>row.map(cellHTML).join('')).join('')}\n      <\/div>\n      <small style=\"color:var(--text-soft);display:block;margin:14px 0;\">You can also type expressions directly \u2014 supports sqrt(), cbrt(), sin\/cos\/tan, asin\/acos\/atan, sinh\/cosh\/tanh, ln, log, log2, n!, nPr(n,r), nCr(n,r), nthroot(x,n), abs(), mod(a,b), pi, e<\/small>\n\n      <div class=\"calc-section section-navy\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Calculation history (this session)<\/h5><\/div>\n        <div id=\"sc-history\"><\/div>\n      <\/div>\n      <\/div>`;\n\n    el.querySelectorAll('input[name=sc-angle]').forEach(r=>r.onchange=(e)=>{ angleMode=e.target.value; });\n\n    function wrapTrailing(prefix,suffix){\n      const expr=$('sc-expr').value;\n      const m=expr.match(\/(\\d+\\.?\\d*)$\/);\n      if(m){\n        const numStr=m[0];\n        const before=expr.slice(0,expr.length-numStr.length);\n        $('sc-expr').value=before+prefix+numStr+suffix;\n      } else {\n        $('sc-expr').value+=prefix;\n      }\n    }\n    function toggleSign(){\n      const expr=$('sc-expr').value;\n      const m=expr.match(\/(-?\\d+\\.?\\d*)$\/);\n      if(m){\n        const numStr=m[0];\n        const before=expr.slice(0,expr.length-numStr.length);\n        $('sc-expr').value=before+(numStr.startsWith('-')?numStr.slice(1):'-'+numStr);\n      }\n    }\n    function doEquals(){\n      const expr=$('sc-expr').value.trim();\n      if(!expr) return;\n      try{\n        const result=evaluate(expr);\n        lastVal=result;\n        history.unshift({expr,result});\n        if(history.length>15) history.pop();\n        $('sc-expr').value=num(result,10);\n        renderHistory();\n      }catch(e){\n        const suggestion=suggestFix(expr);\n        $('sc-history').insertAdjacentHTML('beforebegin', `<div class=\"readout error\" style=\"margin-bottom:14px;\"><div class=\"r-label\">Check your expression<\/div><div class=\"r-value\">${suggestion||'Could not evaluate \u2014 check syntax and parentheses.'}<\/div><\/div>`);\n        setTimeout(()=>{ const e2=el.querySelector('.readout.error'); if(e2) e2.remove(); },3000);\n      }\n    }\n    function currentValueOrEval(){\n      if(lastVal!==null && $('sc-expr').value===num(lastVal,10)) return lastVal;\n      try{ return evaluate($('sc-expr').value||'0'); }catch(e){ return lastVal; }\n    }\n\n    $('sc-grid').addEventListener('click',(e)=>{\n      const btn=e.target.closest('[data-btn]');\n      if(!btn) return;\n      const k=btn.dataset.btn;\n      const expr=()=>$('sc-expr');\n      if(\/^[0-9.]$\/.test(k)){ expr().value+=k; }\n      else if(k==='('||k===')'){ expr().value+=k; }\n      else if(k==='+'||k==='\u2212'||k==='\u00d7'||k==='\u00f7'){ expr().value+=k; }\n      else if(k==='x\u02b8'){ expr().value+='^'; }\n      else if(k==='x\u00b2'){ wrapTrailing('(',')^2'); }\n      else if(k==='x\u00b3'){ wrapTrailing('(',')^3'); }\n      else if(k==='1\/x'){ wrapTrailing('1\/(',')'); }\n      else if(k==='%'){ wrapTrailing('(',')\/100'); }\n      else if(k==='n!'){ expr().value+='!'; }\n      else if(k==='sin'){ expr().value+='sin('; }\n      else if(k==='cos'){ expr().value+='cos('; }\n      else if(k==='tan'){ expr().value+='tan('; }\n      else if(k==='sin\u207b\u00b9'){ expr().value+='asin('; }\n      else if(k==='cos\u207b\u00b9'){ expr().value+='acos('; }\n      else if(k==='tan\u207b\u00b9'){ expr().value+='atan('; }\n      else if(k==='ln'){ expr().value+='ln('; }\n      else if(k==='log'){ expr().value+='log('; }\n      else if(k==='\u221ax'){ expr().value+='sqrt('; }\n      else if(k==='\u00b3\u221ax'){ expr().value+='cbrt('; }\n      else if(k==='\u02b8\u221ax'){ expr().value+='nthroot('; }\n      else if(k==='e\u02e3'){ expr().value+='e^('; }\n      else if(k==='10\u02e3'){ expr().value+='10^('; }\n      else if(k==='\u03c0'){ expr().value+='pi'; }\n      else if(k==='e'){ expr().value+='e'; }\n      else if(k==='EXP'){ expr().value+='*10^('; }\n      else if(k==='\u00b1'){ toggleSign(); }\n      else if(k==='RND'){ const v=currentValueOrEval(); if(v!==null) expr().value=num(Math.round(v),10); }\n      else if(k==='AC'){ expr().value=''; }\n      else if(k==='Back'){ expr().value=expr().value.slice(0,-1); }\n      else if(k==='Ans'){ expr().value+= lastVal!==null?num(lastVal,10):'0'; }\n      else if(k==='M+'){ const v=currentValueOrEval(); if(v!==null){ memory+=v; } }\n      else if(k==='M\u2212'){ const v=currentValueOrEval(); if(v!==null){ memory-=v; } }\n      else if(k==='MR'){ expr().value+=num(memory,10); }\n      else if(k==='='){ doEquals(); }\n      expr().focus();\n    });\n\n    function renderHistory(){\n      $('sc-history').innerHTML = history.length ? history.map(h=>`<div class=\"sc-history-item\" data-e=\"${h.expr.replace(\/\"\/g,'&quot;')}\"><span>${h.expr}<\/span><span>= ${num(h.result,6)}<\/span><\/div>`).join('') : '<p style=\"color:var(--text-soft);font-size:.85rem;\">No calculations yet this session.<\/p>';\n      $('sc-history').querySelectorAll('.sc-history-item').forEach(item=>item.onclick=()=>{ $('sc-expr').value=item.dataset.e; $('sc-expr').focus(); });\n    }\n    $('sc-expr').addEventListener('keydown',(e)=>{ if(e.key==='Enter') doEquals(); });\n    renderHistory();\n  }});\n\n\/* ---------- HEALTH & FITNESS ---------- *\/\nreg({slug:'bmi',name:'BMI Calculator',cat:'health',icn:'scale',photo:30,\n  desc:'A complete health assessment tool: BMI with a visual scale, healthy weight range, target weight planning, waist-to-height ratio, and an integrated calorie dashboard.',\n  render(el){\n    function svgScale(bmi){\n      const min=15, max=40, w=600, h=54;\n      const segs=[[min,18.5,'#94A3B8','Underweight'],[18.5,25,'#1E9E6B','Healthy'],[25,30,'#F5A623','Overweight'],[30,max,'#E0533D','Obesity']];\n      const xOf=v=>((Math.max(min,Math.min(max,v))-min)\/(max-min))*w;\n      let bars=segs.map(([a,b,color])=>`<rect x=\"${xOf(a)}\" y=\"10\" width=\"${xOf(b)-xOf(a)}\" height=\"18\" fill=\"${color}\"\/>`).join('');\n      const markerX=xOf(bmi);\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\">\n        ${bars}\n        <polygon points=\"${markerX-8},${h-2} ${markerX+8},${h-2} ${markerX},${h-22}\" fill=\"var(--ink)\"\/>\n        <text x=\"${markerX}\" y=\"8\" text-anchor=\"middle\" font-family=\"IBM Plex Mono, monospace\" font-size=\"11\" font-weight=\"700\" fill=\"var(--ink)\">${bmi.toFixed(1)}<\/text>\n      <\/svg>`;\n    }\n    el.innerHTML = `\n      <div class=\"bmi-shell\">\n      <div class=\"seg\" id=\"bmi-seg\"><button class=\"active\" data-v=\"metric\">Metric (kg\/cm)<\/button><button data-v=\"imperial\">Imperial (lb\/in)<\/button><\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label id=\"bmi-w-label\">Weight (kg)<\/label><input type=\"number\" id=\"bmi-w\" value=\"72\"><\/div>\n        <div class=\"field\"><label id=\"bmi-h-label\">Height (cm)<\/label><input type=\"number\" id=\"bmi-h\" value=\"175\"><\/div>\n      <\/div>\n      <span class=\"more-link\" id=\"bmi-more-link\">+ Add age, sex &amp; activity for a fuller picture<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"bmi-more-panel\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Age<\/label><input type=\"number\" id=\"bmi-age\" placeholder=\"e.g. 32\"><\/div>\n          <div class=\"field\"><label>Sex<\/label><select id=\"bmi-sex\"><option value=\"\">Prefer not to say<\/option><option value=\"m\">Male<\/option><option value=\"f\">Female<\/option><\/select><\/div>\n        <\/div>\n        <div class=\"field\"><label>Activity level<\/label>\n          <select id=\"bmi-act\">\n            <option value=\"\">Not specified<\/option>\n            <option value=\"1.2\">Sedentary (little\/no exercise)<\/option>\n            <option value=\"1.375\">Light exercise (1-3 days\/wk)<\/option>\n            <option value=\"1.55\">Moderate exercise (3-5 days\/wk)<\/option>\n            <option value=\"1.725\">Heavy exercise (6-7 days\/wk)<\/option>\n            <option value=\"1.9\">Athlete (2x\/day)<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"field\"><label>Waist circumference <small>(optional, same units as height)<\/small><\/label><input type=\"number\" id=\"bmi-waist\" placeholder=\"e.g. 84\"><\/div>\n        <div class=\"field\"><label>Target BMI: <span id=\"bmi-target-val\">22.0<\/span><\/label><input type=\"range\" id=\"bmi-target\" min=\"18.5\" max=\"24.9\" step=\"0.1\" value=\"22\"><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"bmi-btn\">Calculate BMI<\/button>\n\n      <div id=\"bmi-results\"><\/div>\n      <\/div>`;\n    let unit='metric';\n    $('bmi-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('bmi-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); unit=b.dataset.v;\n      $('bmi-w-label').textContent = unit==='metric' ? 'Weight (kg)' : 'Weight (lb)';\n      $('bmi-h-label').textContent = unit==='metric' ? 'Height (cm)' : 'Height (in)';\n    });\n    $('bmi-more-link').onclick=()=>$('bmi-more-panel').classList.toggle('show');\n    $('bmi-target').oninput=e=>$('bmi-target-val').textContent=(+e.target.value).toFixed(1);\n\n    $('bmi-btn').onclick=()=>{\n      let w=+$('bmi-w').value, h=+$('bmi-h').value;\n      const waistRaw=+$('bmi-waist').value||0;\n      if(unit==='imperial'){ w=w*0.453592; h=h*2.54; }\n      const hm=h\/100;\n      if(w<=0||hm<=0){ $('bmi-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid weight and height.<\/div><\/div>'; return; }\n      const bmi=w\/(hm*hm);\n      let cls, insight;\n      if(bmi<18.5){ cls='Underweight'; insight={text:'Your BMI is below the general healthy range. If this is unexpected or comes with other symptoms, consider checking in with a healthcare professional.',color:'#5B7A93'}; }\n      else if(bmi<25){ cls='Healthy weight'; insight={text:'You are within the general healthy BMI range. Regular activity and balanced nutrition can help you stay there.',color:'#1E9E6B'}; }\n      else if(bmi<30){ cls='Overweight'; insight={text:'Your BMI is above the general healthy range. Even modest changes can matter for some people \u2014 it may be worth discussing goals with a healthcare professional.',color:'#F5A623'}; }\n      else { cls='Obesity'; insight={text:'Your BMI is in the range generally associated with higher health risk. A healthcare professional can help you build a plan that fits your specific situation.',color:'#E0533D'}; }\n\n      const lowW=18.5*hm*hm, highW=24.9*hm*hm;\n      const targetBMI=+$('bmi-target').value;\n      const targetW=targetBMI*hm*hm;\n      const diffFromRange = bmi<18.5 ? lowW-w : bmi>24.9 ? w-highW : 0;\n      const diffToTarget = w-targetW;\n\n      const rangeTxt = unit==='metric' ? `${num(lowW,1)}\u2013${num(highW,1)} kg` : `${num(lowW\/0.453592,1)}\u2013${num(highW\/0.453592,1)} lb`;\n      const wUnit = unit==='metric' ? 'kg' : 'lb';\n      const wDisp = v => unit==='metric' ? num(v,1) : num(v\/0.453592,1);\n\n      $('bmi-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${bmi.toFixed(1)}<\/div><div class=\"dd-main-sub\">${cls} \u00b7 Healthy range: ${rangeTxt}<\/div><\/div>\n        <div class=\"bmi-scale-wrap\">${svgScale(bmi)}\n          <div class=\"bmi-scale-labels\"><span>15<\/span><span>18.5<\/span><span>25<\/span><span>30<\/span><span>40<\/span><\/div>\n        <\/div>\n\n        <div class=\"gpa-insight\" style=\"background:${insight.color}18;border:1px solid ${insight.color}44;\">\n          <b style=\"color:${insight.color};\">Your result:<\/b> ${insight.text}\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('scale',16)}<\/div><h5>Fitness dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>BMI<\/span><b>${bmi.toFixed(1)}<\/b><\/div>\n            <div class=\"m-item\"><span>Category<\/span><b>${cls}<\/b><\/div>\n            <div class=\"m-item\"><span>Current weight<\/span><b>${wDisp(w)} ${wUnit}<\/b><\/div>\n            <div class=\"m-item\"><span>Healthy weight range<\/span><b>${rangeTxt}<\/b><\/div>\n            ${diffFromRange>0?`<div class=\"m-item\"><span>${bmi<18.5?'Below range by':'Above range by'}<\/span><b>${wDisp(diffFromRange)} ${wUnit}<\/b><\/div>`:''}\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Target weight planner<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">At a target BMI of <b>${targetBMI.toFixed(1)}<\/b>, your estimated target weight is:<\/p>\n          <div class=\"dd-main-result\" style=\"margin-top:10px;\"><div class=\"dd-main-value\">${wDisp(targetW)} ${wUnit}<\/div><div class=\"dd-main-sub\">${Math.abs(diffToTarget)<0.5?'You are already close to this target':(diffToTarget>0?`About ${wDisp(diffToTarget)} ${wUnit} below your current weight`:`About ${wDisp(-diffToTarget)} ${wUnit} above your current weight`)}<\/div><\/div>\n        <\/div>\n\n        ${waistRaw>0?(()=>{ const waistCm = unit==='metric'?waistRaw:waistRaw*2.54; const ratio=waistCm\/h;\n          const ratioNote = ratio<0.5 ? 'Generally considered a lower-risk range.' : 'Generally associated with increased health risk in population studies \u2014 consider discussing with a healthcare professional if you have concerns.';\n          return `<div class=\"calc-section section-amber\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>Waist-to-height ratio<\/h5><\/div>\n            <div class=\"summary-mini\"><div class=\"m-item\"><span>Ratio<\/span><b>${ratio.toFixed(2)}<\/b><\/div><div class=\"m-item\"><span>General guide<\/span><b>${ratio<0.5?'Below 0.5':'0.5 or above'}<\/b><\/div><\/div>\n            <p style=\"font-size:.82rem;color:var(--text-soft);margin-top:10px;\">${ratioNote} This is a general population indicator, not a diagnosis.<\/p>\n          <\/div>`; })():''}\n\n        ${(()=>{ const age=+$('bmi-age').value, sex=$('bmi-sex').value, act=$('bmi-act').value;\n          if(!age||!sex) return '';\n          const bmr = sex==='m' ? 10*w+6.25*h-5*age+5 : 10*w+6.25*h-5*age-161;\n          const maint = act ? bmr*(+act) : null;\n          return `<div class=\"calc-section section-navy\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Estimated energy needs<\/h5><\/div>\n            <div class=\"summary-mini\">\n              <div class=\"m-item\"><span>BMR (resting)<\/span><b>${num(bmr,0)} cal\/day<\/b><\/div>\n              ${maint?`<div class=\"m-item\"><span>Maintenance calories<\/span><b>${num(maint,0)} cal\/day<\/b><\/div>`:''}\n            <\/div>\n            <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:10px;\">Estimates using the Mifflin-St Jeor formula. See the <a href=\"#\/calculator\/bmr\">BMR Calculator<\/a> and <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for more detail.<\/p>\n          <\/div>`; })()}\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"bmi-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"bmi-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Your measurements are calculated locally in your browser and are not stored. BMI is a general screening tool, not a diagnosis \u2014 consult a healthcare professional for personalized advice.<\/p>`;\n\n      const shareText=`My BMI is ${bmi.toFixed(1)} (${cls}). Healthy range: ${rangeTxt}. Calculated on MAFtrix.`;\n      $('bmi-copy').onclick=()=>{ const done=()=>{$('bmi-copy').textContent='Copied!';setTimeout(()=>$('bmi-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('bmi-print').onclick=()=>window.print();\n    };\n    $('bmi-btn').click();\n  }});\n\nreg({slug:'bmr',name:'BMR Calculator',cat:'health',icn:'activity',photo:31,\n  desc:'A complete metabolism hub: multiple BMR formulas, TDEE by activity level, goal-based calorie planning, macro suggestions, and a weight-goal timeline.',\n  render(el){\n    function calcBMR(formula,w,h,a,s,bf){\n      if(formula==='mifflin') return s==='m' ? 10*w+6.25*h-5*a+5 : 10*w+6.25*h-5*a-161;\n      if(formula==='harris') return s==='m' ? 88.362+13.397*w+4.799*h-5.677*a : 447.593+9.247*w+3.098*h-4.330*a;\n      if(formula==='katch'){ if(!bf) return null; const lbm=w*(1-bf\/100); return 370+21.6*lbm; }\n      return 0;\n    }\n    const ACTIVITY=[['Sedentary (little\/no exercise)',1.2],['Light exercise (1-3 days\/wk)',1.375],['Moderate exercise (3-5 days\/wk)',1.55],['Heavy exercise (6-7 days\/wk)',1.725],['Athlete (2x\/day)',1.9]];\n    const GOALS=[['Maintain weight',0],['Mild weight loss',-250],['Moderate weight loss',-500],['Aggressive weight loss',-750],['Mild weight gain',250],['Lean muscle gain',500]];\n    const MACRO_STYLES={balanced:{label:'Balanced',p:0.3,c:0.4,f:0.3},highprotein:{label:'High protein',p:0.4,c:0.3,f:0.3},lowcarb:{label:'Lower carb',p:0.35,c:0.25,f:0.4},highcarb:{label:'Higher carb',p:0.2,c:0.55,f:0.25}};\n    const ACTIVITIES_MET=[['Walking (brisk)',3.5],['Running (6mph)',9.8],['Cycling (moderate)',7.5],['Swimming (moderate)',6.0]];\n\n    el.innerHTML = `\n      <div class=\"bmr-shell\">\n      <div class=\"seg\" id=\"bmr-unit-seg\"><button class=\"active\" data-v=\"metric\">Metric (kg\/cm)<\/button><button data-v=\"imperial\">Imperial (lb\/in)<\/button><\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label id=\"bmr-w-label\">Weight (kg)<\/label><input type=\"number\" id=\"bmr-w\" value=\"70\"><\/div>\n        <div class=\"field\"><label id=\"bmr-h-label\">Height (cm)<\/label><input type=\"number\" id=\"bmr-h\" value=\"170\"><\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Age<\/label><input type=\"number\" id=\"bmr-a\" value=\"28\"><\/div>\n        <div class=\"field\"><label>Sex<\/label><select id=\"bmr-s\"><option value=\"m\">Male<\/option><option value=\"f\">Female<\/option><\/select><\/div>\n      <\/div>\n      <div class=\"field\"><label>Formula<\/label>\n        <select id=\"bmr-formula\">\n          <option value=\"mifflin\">Mifflin-St Jeor (recommended default)<\/option>\n          <option value=\"harris\">Harris-Benedict (Revised)<\/option>\n          <option value=\"katch\">Katch-McArdle (needs body fat %)<\/option>\n        <\/select>\n      <\/div>\n      <span class=\"more-link\" id=\"bmr-more-link\">+ Body fat %, goals &amp; macro preferences<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"bmr-more-panel\">\n        <div class=\"field\"><label>Body fat % <small>(optional \u2014 enables Katch-McArdle &amp; body composition)<\/small><\/label><input type=\"number\" id=\"bmr-bf\" placeholder=\"e.g. 22\"><\/div>\n        <div class=\"section-tag\">Weight goal timeline<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Target weight<\/label><input type=\"number\" id=\"bmr-target-w\" placeholder=\"e.g. 65\"><\/div>\n          <div class=\"field\"><label>Daily calorie change<\/label><input type=\"number\" id=\"bmr-daily-change\" placeholder=\"e.g. -500\"><\/div>\n        <\/div>\n        <div class=\"section-tag\">Macro style<\/div>\n        <div class=\"field\"><label>Style<\/label><select id=\"bmr-macro-style\">${Object.entries(MACRO_STYLES).map(([k,v])=>`<option value=\"${k}\">${v.label}<\/option>`).join('')}<\/select><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"bmr-btn\">Calculate BMR<\/button>\n\n      <div id=\"bmr-results\"><\/div>\n      <\/div>`;\n\n    let unit='metric';\n    $('bmr-unit-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('bmr-unit-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); unit=b.dataset.v;\n      $('bmr-w-label').textContent = unit==='metric'?'Weight (kg)':'Weight (lb)';\n      $('bmr-h-label').textContent = unit==='metric'?'Height (cm)':'Height (in)';\n    });\n    $('bmr-more-link').onclick=()=>$('bmr-more-panel').classList.toggle('show');\n\n    $('bmr-btn').onclick=()=>{\n      let w=+$('bmr-w').value, h=+$('bmr-h').value;\n      const a=+$('bmr-a').value, s=$('bmr-s').value, bf=+$('bmr-bf').value||0;\n      if(unit==='imperial'){ w=w*0.453592; h=h*2.54; }\n      if(w<=0||h<=0||a<=0){ $('bmr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter valid weight, height and age.<\/div><\/div>'; return; }\n\n      const formula=$('bmr-formula').value;\n      let bmr=calcBMR(formula,w,h,a,s,bf);\n      let formulaLabel={mifflin:'Mifflin-St Jeor',harris:'Harris-Benedict (Revised)',katch:'Katch-McArdle'}[formula];\n      if(bmr===null){ bmr=calcBMR('mifflin',w,h,a,s,bf); formulaLabel='Mifflin-St Jeor (Katch-McArdle needs body fat %)'; }\n\n      const mifflin=calcBMR('mifflin',w,h,a,s,bf), harris=calcBMR('harris',w,h,a,s,bf), katch=calcBMR('katch',w,h,a,s,bf);\n\n      $('bmr-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(bmr,0)} cal\/day<\/div><div class=\"dd-main-sub\">Estimated BMR \u00b7 ${formulaLabel}<\/div><\/div>\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:10px;\">This is an estimate based on the formula and inputs you provided \u2014 actual metabolic rate varies by individual.<\/p>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Formula comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Formula<\/th><th>Estimated BMR<\/th><\/tr><\/thead><tbody>\n            <tr><td>Mifflin-St Jeor<\/td><td>${num(mifflin,0)} cal\/day<\/td><\/tr>\n            <tr><td>Harris-Benedict (Revised)<\/td><td>${num(harris,0)} cal\/day<\/td><\/tr>\n            <tr><td>Katch-McArdle<\/td><td>${katch!==null?num(katch,0)+' cal\/day':'Requires body fat %'}<\/td><\/tr>\n          <\/tbody><\/table>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Mifflin-St Jeor is generally considered the most broadly accurate for most people today. Katch-McArdle can be more precise if you know your body fat percentage, since it accounts for lean mass directly.<\/p>\n        <\/div>\n\n        ${bf>0?(()=>{ const lbm=w*(1-bf\/100), fatMass=w-lbm;\n          return `<div class=\"calc-section section-green\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('scale',16)}<\/div><h5>Body composition<\/h5><\/div>\n            <div class=\"summary-mini\">\n              <div class=\"m-item\"><span>Lean body mass<\/span><b>${num(unit==='metric'?lbm:lbm\/0.453592,1)} ${unit==='metric'?'kg':'lb'}<\/b><\/div>\n              <div class=\"m-item\"><span>Fat mass<\/span><b>${num(unit==='metric'?fatMass:fatMass\/0.453592,1)} ${unit==='metric'?'kg':'lb'}<\/b><\/div>\n              <div class=\"m-item\"><span>Body fat<\/span><b>${bf}%<\/b><\/div>\n            <\/div>\n            <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">See the <a href=\"#\/calculator\/body-fat\">Body Fat Calculator<\/a> if you need to estimate this from measurements.<\/p>\n          <\/div>`; })():''}\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Daily calorie needs (TDEE) by activity level<\/h5><\/div>\n          <div class=\"gpa-bar-chart\">${ACTIVITY.map(([label,mult])=>`<div class=\"bar-col\"><div class=\"bar-val\">${num(bmr*mult,0)}<\/div><div class=\"bar\" style=\"height:${Math.max(4,(bmr*mult)\/(bmr*1.9)*130)}px;\"><\/div><div class=\"bar-label\">${label.split(' (')[0]}<\/div><\/div>`).join('')}<\/div>\n          <table class=\"ref-table\" style=\"margin-top:14px;\"><thead><tr><th>Activity level<\/th><th>Calories\/day<\/th><\/tr><\/thead><tbody>\n            ${ACTIVITY.map(([l,m])=>`<tr><td>${l}<\/td><td>${num(bmr*m,0)}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Goal-based calorie planner<\/h5><\/div>\n          <div class=\"field\"><label>Based on activity level<\/label>\n            <select id=\"bmr-goal-activity\">${ACTIVITY.map(([l,m],i)=>`<option value=\"${m}\" ${i===1?'selected':''}>${l}<\/option>`).join('')}<\/select>\n          <\/div>\n          <div id=\"bmr-goal-table\"><\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Very low-calorie targets should be discussed with a healthcare professional rather than pursued independently.<\/p>\n        <\/div>\n\n        ${(+$('bmr-target-w').value)?(()=>{ const targetW=+$('bmr-target-w').value, dailyChange=+$('bmr-daily-change').value||-500;\n          const targetWkg = unit==='metric'?targetW:targetW*0.453592;\n          const diffKg = w-targetWkg;\n          if(dailyChange===0||Math.sign(dailyChange)===Math.sign(diffKg)) return '<div class=\"calc-section section-blue\"><div class=\"calc-section-head\"><div class=\"cs-icn\">'+icon('calendar',16)+'<\/div><h5>Weight goal timeline<\/h5><\/div><p style=\"color:var(--text-soft);font-size:.88rem;\">Enter a daily calorie change in the same direction as your goal (negative to lose, positive to gain) to see an estimated timeline.<\/p><\/div>';\n          const weeklyChangeKg=(dailyChange*7)\/7700;\n          const weeksNeeded=Math.abs(diffKg\/weeklyChangeKg);\n          const targetDate=new Date(Date.now()+weeksNeeded*7*86400000);\n          return `<div class=\"calc-section section-blue\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('calendar',16)}<\/div><h5>Weight goal timeline<\/h5><\/div>\n            <div class=\"summary-mini\">\n              <div class=\"m-item\"><span>Weight to change<\/span><b>${num(Math.abs(diffKg),1)} ${unit==='metric'?'kg':'lb'}<\/b><\/div>\n              <div class=\"m-item\"><span>Estimated weekly change<\/span><b>${num(Math.abs(weeklyChangeKg),2)} kg<\/b><\/div>\n              <div class=\"m-item\"><span>Estimated time<\/span><b>${num(weeksNeeded,1)} weeks<\/b><\/div>\n              <div class=\"m-item\"><span>Approx. target date<\/span><b>${targetDate.toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'})}<\/b><\/div>\n            <\/div>\n            <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Based on the common approximation that roughly 7,700 kcal corresponds to 1 kg of body weight change. This is an estimate, not a guarantee \u2014 actual results vary by individual.<\/p>\n          <\/div>`; })():''}\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Macro suggestions<\/h5><\/div>\n          <div id=\"bmr-macro-table\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Calories burned comparison<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">Approximate time to burn the equivalent of one day's BMR (${num(bmr,0)} cal) through common activities:<\/p>\n          <table class=\"ref-table\"><thead><tr><th>Activity<\/th><th>Approx. time needed<\/th><\/tr><\/thead><tbody>\n            ${ACTIVITIES_MET.map(([name,met])=>{ const calPerMin=met*3.5*w\/200; const mins=bmr\/calPerMin; const hrs=Math.floor(mins\/60), remMin=Math.round(mins%60);\n              return `<tr><td>${name}<\/td><td>${hrs>0?hrs+'h ':''}${remMin}m<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>What-if simulator<\/h5><\/div>\n          <div class=\"field\"><label>Hypothetical weight: <span id=\"bmr-sim-w-val\">${num(w,0)}<\/span> ${unit==='metric'?'kg':'lb'}<\/label><input type=\"range\" id=\"bmr-sim-w\" min=\"${Math.max(30,w-30)}\" max=\"${w+30}\" value=\"${w}\"><\/div>\n          <div id=\"bmr-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"bmr-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"bmr-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Your information is calculated locally in your browser and is not stored. These are general estimates, not personalized medical advice.<\/p>`;\n\n      function renderGoalTable(){\n        const mult=+$('bmr-goal-activity').value;\n        const tdee=bmr*mult;\n        $('bmr-goal-table').innerHTML = `<table class=\"ref-table\"><thead><tr><th>Goal<\/th><th>Target calories\/day<\/th><\/tr><\/thead><tbody>\n          ${GOALS.map(([label,delta])=>`<tr><td>${label}<\/td><td>${num(tdee+delta,0)}<\/td><\/tr>`).join('')}\n        <\/tbody><\/table>`;\n      }\n      $('bmr-goal-activity').onchange=renderGoalTable; renderGoalTable();\n\n      function renderMacros(){\n        const mult=+$('bmr-goal-activity').value, tdee=bmr*mult;\n        const style=MACRO_STYLES[$('bmr-macro-style').value];\n        const pGrams=(tdee*style.p)\/4, cGrams=(tdee*style.c)\/4, fGrams=(tdee*style.f)\/9;\n        $('bmr-macro-table').innerHTML = `<p style=\"color:var(--text-soft);font-size:.85rem;\">At maintenance calories (${num(tdee,0)}\/day) using a ${style.label.toLowerCase()} split:<\/p>\n          <div class=\"summary-mini\"><div class=\"m-item\"><span>Protein<\/span><b>${num(pGrams,0)}g<\/b><\/div><div class=\"m-item\"><span>Carbohydrates<\/span><b>${num(cGrams,0)}g<\/b><\/div><div class=\"m-item\"><span>Fat<\/span><b>${num(fGrams,0)}g<\/b><\/div><\/div>`;\n      }\n      $('bmr-macro-style').onchange=renderMacros; renderMacros();\n\n      function simUpdate(){\n        const simW=+$('bmr-sim-w').value;\n        $('bmr-sim-w-val').textContent=num(simW,0);\n        const simWkg = unit==='metric'?simW:simW*0.453592;\n        const simBMR=calcBMR(formula==='katch'&&!bf?'mifflin':formula,simWkg,h,a,s,bf);\n        const simTDEE=simBMR*1.375;\n        $('bmr-sim-result').innerHTML = `<div class=\"m-item\"><span>Simulated BMR<\/span><b>${num(simBMR,0)} cal\/day<\/b><\/div><div class=\"m-item\"><span>Simulated TDEE (light activity)<\/span><b>${num(simTDEE,0)} cal\/day<\/b><\/div><div class=\"m-item\"><span>Change from current<\/span><b>${simBMR>bmr?'+':''}${num(simBMR-bmr,0)} cal\/day<\/b><\/div>`;\n      }\n      $('bmr-sim-w').oninput=simUpdate; simUpdate();\n\n      const shareText=`My estimated BMR is ${num(bmr,0)} cal\/day (${formulaLabel}). Calculated on MAFtrix.`;\n      $('bmr-copy').onclick=()=>{ const done=()=>{$('bmr-copy').textContent='Copied!';setTimeout(()=>$('bmr-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('bmr-print').onclick=()=>window.print();\n    };\n    $('bmr-btn').click();\n  }});\n\nreg({slug:'calorie',name:'Calorie Calculator',cat:'health',icn:'activity',photo:32,\n  desc:'A complete nutrition planning hub: daily calorie needs, multi-pace weight-loss timelines, macro styles, meal distribution, water intake, and food-energy comparisons.',\n  render(el){\n    const GOALS=[['Maintain weight',0],['Mild loss',-250],['Moderate loss',-500],['Aggressive loss',-750],['Mild gain',250],['Lean muscle gain',500]];\n    const MACRO_STYLES={balanced:{label:'Balanced',p:0.3,c:0.4,f:0.3},highprotein:{label:'High protein',p:0.4,c:0.3,f:0.3},lowcarb:{label:'Low carb',p:0.35,c:0.25,f:0.4},highcarb:{label:'High carb',p:0.2,c:0.55,f:0.25},mediterranean:{label:'Mediterranean-style',p:0.2,c:0.45,f:0.35},higherfat:{label:'Higher fat',p:0.25,c:0.2,f:0.55}};\n    const FOODS=[['Slice of pizza',285],['Chocolate bar',235],['Can of soda',150],['Bagel with cream cheese',350],['Cheeseburger',550]];\n    const ACTIVITIES_MET=[['Walking',3.5],['Running',9.8],['Cycling',7.5]];\n\n    el.innerHTML = `\n      <div class=\"cal-shell\">\n      <div class=\"seg\" id=\"cal-unit-seg\"><button class=\"active\" data-v=\"metric\">Metric (kg\/cm)<\/button><button data-v=\"imperial\">Imperial (lb\/in)<\/button><\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label id=\"cal-w-label\">Weight (kg)<\/label><input type=\"number\" id=\"cal-w\" value=\"70\"><\/div>\n        <div class=\"field\"><label id=\"cal-h-label\">Height (cm)<\/label><input type=\"number\" id=\"cal-h\" value=\"170\"><\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Age<\/label><input type=\"number\" id=\"cal-a\" value=\"28\"><\/div>\n        <div class=\"field\"><label>Sex<\/label><select id=\"cal-s\"><option value=\"m\">Male<\/option><option value=\"f\">Female<\/option><\/select><\/div>\n      <\/div>\n      <div class=\"field\"><label>Activity level<\/label>\n        <select id=\"cal-act\">\n          <option value=\"1.2\">Sedentary (little\/no exercise)<\/option>\n          <option value=\"1.375\" selected>Light exercise (1-3 days\/wk)<\/option>\n          <option value=\"1.55\">Moderate exercise (3-5 days\/wk)<\/option>\n          <option value=\"1.725\">Heavy exercise (6-7 days\/wk)<\/option>\n          <option value=\"1.9\">Athlete (2x\/day)<\/option>\n        <\/select>\n      <\/div>\n      <div class=\"field\"><label>Primary goal<\/label>\n        <select id=\"cal-goal\">${GOALS.map(([l,d],i)=>`<option value=\"${d}\" ${i===0?'selected':''}>${l}<\/option>`).join('')}<\/select>\n      <\/div>\n      <span class=\"more-link\" id=\"cal-more-link\">+ Weight-loss timeline &amp; macro style<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"cal-more-panel\">\n        <div class=\"section-tag\">Weight-loss timeline<\/div>\n        <div class=\"field\"><label>Amount to lose\/gain <small>(same units as weight)<\/small><\/label><input type=\"number\" id=\"cal-goal-amount\" placeholder=\"e.g. 8\"><\/div>\n        <div class=\"section-tag\">Macro style<\/div>\n        <div class=\"field\"><label>Style<\/label><select id=\"cal-macro-style\">${Object.entries(MACRO_STYLES).map(([k,v])=>`<option value=\"${k}\">${v.label}<\/option>`).join('')}<\/select><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"cal-btn\">Calculate calories<\/button>\n\n      <div id=\"cal-results\"><\/div>\n      <\/div>`;\n\n    let unit='metric';\n    $('cal-unit-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('cal-unit-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); unit=b.dataset.v;\n      $('cal-w-label').textContent = unit==='metric'?'Weight (kg)':'Weight (lb)';\n      $('cal-h-label').textContent = unit==='metric'?'Height (cm)':'Height (in)';\n    });\n    $('cal-more-link').onclick=()=>$('cal-more-panel').classList.toggle('show');\n\n    $('cal-btn').onclick=()=>{\n      let w=+$('cal-w').value, h=+$('cal-h').value;\n      const a=+$('cal-a').value, s=$('cal-s').value, act=+$('cal-act').value, goal=+$('cal-goal').value;\n      if(unit==='imperial'){ w=w*0.453592; h=h*2.54; }\n      if(w<=0||h<=0||a<=0){ $('cal-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter valid weight, height and age.<\/div><\/div>'; return; }\n\n      const bmr = s==='m' ? 10*w+6.25*h-5*a+5 : 10*w+6.25*h-5*a-161;\n      const tdee = bmr*act;\n      const target = tdee+goal;\n      const deficitPct = Math.abs(goal)\/tdee*100;\n\n      $('cal-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(target,0)} cal\/day<\/div><div class=\"dd-main-sub\">Target calories for your selected goal<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Energy dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>BMR (resting)<\/span><b>${num(bmr,0)} cal<\/b><\/div>\n            <div class=\"m-item\"><span>TDEE (maintenance)<\/span><b>${num(tdee,0)} cal<\/b><\/div>\n            <div class=\"m-item\"><span>Target calories<\/span><b>${num(target,0)} cal<\/b><\/div>\n            <div class=\"m-item\"><span>Daily adjustment<\/span><b>${goal>=0?'+':''}${goal} cal<\/b><\/div>\n          <\/div>\n          ${goal!==0?`<div class=\"cal-deficit-bar\"><div class=\"cal-deficit-fill\" style=\"width:${Math.min(100,100-deficitPct)}%;background:${goal<0?'#E0533D':'#1E9E6B'};\">${goal<0?'Deficit':'Surplus'}: ${Math.abs(goal)} cal (${num(deficitPct,0)}% of maintenance)<\/div><\/div>`:''}\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>All goals at a glance<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Goal<\/th><th>Calories\/day<\/th><\/tr><\/thead><tbody>\n            ${GOALS.map(([label,delta])=>{ const cals=tdee+delta; const low = cals<1200; return `<tr><td>${label}<\/td><td>${num(cals,0)}${low?' \u26a0':''}<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">\u26a0 marks targets below 1,200 cal\/day \u2014 very low-calorie targets should be discussed with a healthcare professional rather than pursued independently.<\/p>\n        <\/div>\n\n        ${(+$('cal-goal-amount').value)?(()=>{ const amount=+$('cal-goal-amount').value;\n          const paces=[['Mild',250],['Moderate',500],['Faster',750]];\n          const rows=paces.map(([label,delta])=>{\n            const weeklyKg=(delta*7)\/7700;\n            const amountKg=unit==='metric'?amount:amount*0.453592;\n            const weeks=amountKg\/weeklyKg;\n            const date=new Date(Date.now()+weeks*7*86400000);\n            return `<tr><td>${label} (${delta} cal\/day)<\/td><td>${num(weeks,1)} weeks<\/td><td>${date.toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'})}<\/td><\/tr>`;\n          }).join('');\n          return `<div class=\"calc-section section-amber\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('calendar',16)}<\/div><h5>Weight-loss timeline comparison<\/h5><\/div>\n            <table class=\"ref-table\"><thead><tr><th>Pace<\/th><th>Estimated time<\/th><th>Approx. date<\/th><\/tr><\/thead><tbody>${rows}<\/tbody><\/table>\n            <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">Based on the common approximation that ~7,700 kcal corresponds to 1 kg of body weight change. Estimates only \u2014 individual results vary.<\/p>\n          <\/div>`; })():''}\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Macronutrient targets<\/h5><\/div>\n          <div id=\"cal-macro-table\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Suggested meal distribution<\/h5><\/div>\n          <div id=\"cal-meals\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Water intake estimate<\/h5><\/div>\n          ${(()=>{ const waterMl = w*35 + (act>1.4?350:0); return `<div class=\"dd-main-result\" style=\"margin-top:0;\"><div class=\"dd-main-value\">${(waterMl\/1000).toFixed(1)} L\/day<\/div><div class=\"dd-main-sub\">\u2248 ${Math.round(waterMl\/250)} glasses (250ml each)<\/div><\/div><p style=\"font-size:.75rem;color:var(--text-soft);margin-top:10px;\">A general estimate based on body weight and activity \u2014 individual needs vary with climate, health and diet. See the Water Intake Calculator for more detail.<\/p>`; })()}\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Food energy in context<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">Approximate walking time to burn off common foods (illustrative, not prescriptive):<\/p>\n          <table class=\"ref-table\"><thead><tr><th>Food<\/th><th>Calories<\/th><th>Approx. walking time<\/th><\/tr><\/thead><tbody>\n            ${FOODS.map(([name,cals])=>{ const calPerMin=ACTIVITIES_MET[0][1]*3.5*w\/200; const mins=cals\/calPerMin; const hrs=Math.floor(mins\/60), rem=Math.round(mins%60);\n              return `<tr><td>${name}<\/td><td>${cals} cal<\/td><td>${hrs>0?hrs+'h ':''}${rem}m<\/td><\/tr>`; }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"cal-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"cal-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Your information is calculated locally in your browser and is not stored. These are general estimates, not personalized medical or nutrition advice.<\/p>`;\n\n      function renderMacros(){\n        const style=MACRO_STYLES[$('cal-macro-style').value];\n        const pGrams=(target*style.p)\/4, cGrams=(target*style.c)\/4, fGrams=(target*style.f)\/9;\n        $('cal-macro-table').innerHTML = `<p style=\"color:var(--text-soft);font-size:.85rem;\">At ${num(target,0)} cal\/day using a ${style.label.toLowerCase()} split:<\/p>\n          <div class=\"summary-mini\"><div class=\"m-item\"><span>Protein<\/span><b>${num(pGrams,0)}g<\/b><\/div><div class=\"m-item\"><span>Carbohydrates<\/span><b>${num(cGrams,0)}g<\/b><\/div><div class=\"m-item\"><span>Fat<\/span><b>${num(fGrams,0)}g<\/b><\/div><\/div>`;\n      }\n      $('cal-macro-style').onchange=renderMacros; renderMacros();\n\n      const MEALS=[['Breakfast',0.25],['Lunch',0.3],['Dinner',0.3],['Snacks',0.15]];\n      $('cal-meals').innerHTML = MEALS.map(([name,pct])=>`<div class=\"cal-meal-row\"><span>${name}<\/span><div class=\"meal-bar\"><div class=\"meal-fill\" style=\"width:${pct*100}%;\"><\/div><\/div><b>${num(target*pct,0)} cal<\/b><\/div>`).join('');\n\n      const shareText=`My target calorie intake is ${num(target,0)} cal\/day (TDEE: ${num(tdee,0)}, BMR: ${num(bmr,0)}). Calculated on MAFtrix.`;\n      $('cal-copy').onclick=()=>{ const done=()=>{$('cal-copy').textContent='Copied!';setTimeout(()=>$('cal-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('cal-print').onclick=()=>window.print();\n    };\n    $('cal-btn').click();\n  }});\n\nreg({slug:'body-fat',name:'Body Fat Calculator',cat:'health',icn:'scale',photo:33,\n  desc:'A complete body composition hub: U.S. Navy and BMI-based method comparison, fat\/lean mass breakdown, goal planning, FFMI, and integrated energy needs.',\n  render(el){\n    const CATS_M=[[0,6,'Essential fat'],[6,14,'Athletes'],[14,18,'Fitness'],[18,25,'Average'],[25,100,'High']];\n    const CATS_F=[[0,14,'Essential fat'],[14,21,'Athletes'],[21,25,'Fitness'],[25,32,'Average'],[32,100,'High']];\n    function catOf(bf,sex){ const bands=sex==='m'?CATS_M:CATS_F; return bands.find(([lo,hi])=>bf>=lo&&bf<hi)?.[2]||'\u2014'; }\n    function navyBF(sex,h,neck,waist,hip){\n      if(sex==='m') return 495\/(1.0324-0.19077*Math.log10(waist-neck)+0.15456*Math.log10(h))-450;\n      return 495\/(1.29579-0.35004*Math.log10(waist+hip-neck)+0.22100*Math.log10(h))-450;\n    }\n    function bmiBF(sex,bmi,age){ return 1.20*bmi + 0.23*age - 10.8*(sex==='m'?1:0) - 5.4; }\n    function svgScale(bf,sex){\n      const bands=sex==='m'?CATS_M:CATS_F, max=40, w=600, h=54;\n      const colors=['#5B7A93','#1E9E6B','#0EA5A0','#F5A623','#E0533D'];\n      const xOf=v=>(Math.max(0,Math.min(max,v))\/max)*w;\n      let bars=bands.map(([lo,hi],i)=>`<rect x=\"${xOf(lo)}\" y=\"10\" width=\"${xOf(Math.min(hi,max))-xOf(lo)}\" height=\"18\" fill=\"${colors[i]}\"\/>`).join('');\n      const markerX=xOf(bf);\n      return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"100%\" height=\"${h}\">${bars}<polygon points=\"${markerX-8},${h-2} ${markerX+8},${h-2} ${markerX},${h-22}\" fill=\"var(--ink)\"\/><text x=\"${markerX}\" y=\"8\" text-anchor=\"middle\" font-family=\"IBM Plex Mono, monospace\" font-size=\"11\" font-weight=\"700\" fill=\"var(--ink)\">${bf.toFixed(1)}%<\/text><\/svg>`;\n    }\n\n    el.innerHTML = `\n      <div class=\"bf-shell\">\n      <div class=\"seg\" id=\"bf-unit-seg\"><button class=\"active\" data-v=\"metric\">Metric (kg\/cm)<\/button><button data-v=\"imperial\">Imperial (lb\/in)<\/button><\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Sex<\/label><select id=\"bf-s\"><option value=\"m\">Male<\/option><option value=\"f\">Female<\/option><\/select><\/div>\n        <div class=\"field\"><label>Age<\/label><input type=\"number\" id=\"bf-age\" value=\"30\"><\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label id=\"bf-h-label\">Height (cm)<\/label><input type=\"number\" id=\"bf-h\" value=\"175\"><\/div>\n        <div class=\"field\"><label id=\"bf-w-label\">Weight (kg)<\/label><input type=\"number\" id=\"bf-w\" value=\"78\"><\/div>\n      <\/div>\n      <span class=\"more-link\" id=\"bf-more-link\">+ Add tape measurements for the U.S. Navy method<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"bf-more-panel\">\n        <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:0;\">Measure neck just below the larynx, waist at the navel, and hips at the widest point \u2014 a soft tape measure works best.<\/p>\n        <div class=\"form-row\">\n          <div class=\"field\"><label id=\"bf-neck-label\">Neck (cm)<\/label><input type=\"number\" id=\"bf-neck\" value=\"38\"><\/div>\n          <div class=\"field\"><label id=\"bf-waist-label\">Waist (cm)<\/label><input type=\"number\" id=\"bf-waist\" value=\"85\"><\/div>\n        <\/div>\n        <div class=\"field\" id=\"bf-hip-field\"><label id=\"bf-hip-label\">Hip (cm) <small>\u2014 required for female<\/small><\/label><input type=\"number\" id=\"bf-hip\" value=\"95\"><\/div>\n        <div class=\"section-tag\">Goal planner<\/div>\n        <div class=\"field\"><label>Target body fat %<\/label><input type=\"number\" id=\"bf-target\" placeholder=\"e.g. 18\"><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"bf-btn\">Calculate body composition<\/button>\n\n      <div id=\"bf-results\"><\/div>\n      <\/div>`;\n\n    let unit='metric';\n    $('bf-unit-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('bf-unit-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); unit=b.dataset.v;\n      $('bf-h-label').textContent=unit==='metric'?'Height (cm)':'Height (in)';\n      $('bf-w-label').textContent=unit==='metric'?'Weight (kg)':'Weight (lb)';\n      $('bf-neck-label').textContent=unit==='metric'?'Neck (cm)':'Neck (in)';\n      $('bf-waist-label').textContent=unit==='metric'?'Waist (cm)':'Waist (in)';\n      $('bf-hip-label').innerHTML=unit==='metric'?'Hip (cm) <small>\u2014 required for female<\/small>':'Hip (in) <small>\u2014 required for female<\/small>';\n    });\n    $('bf-more-link').onclick=()=>$('bf-more-panel').classList.toggle('show');\n\n    $('bf-btn').onclick=()=>{\n      const s=$('bf-s').value, age=+$('bf-age').value;\n      let h=+$('bf-h').value, w=+$('bf-w').value, neck=+$('bf-neck').value||0, waist=+$('bf-waist').value||0, hip=+$('bf-hip').value||0;\n      if(unit==='imperial'){ h*=2.54; w*=0.453592; neck*=2.54; waist*=2.54; hip*=2.54; }\n      if(h<=0||w<=0||age<=0){ $('bf-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter valid height, weight and age.<\/div><\/div>'; return; }\n\n      const hm=h\/100, bmi=w\/(hm*hm);\n      const bmiEstimate=bmiBF(s,bmi,age);\n      let navyEstimate=null;\n      if(neck>0 && waist>0 && (s==='m'||hip>0)){\n        try{ const v=navyBF(s,h,neck,waist,hip); if(isFinite(v)&&v>0&&v<70) navyEstimate=v; }catch(e){}\n      }\n      const primary = navyEstimate!==null ? navyEstimate : bmiEstimate;\n      const methodLabel = navyEstimate!==null ? 'U.S. Navy method' : 'BMI-based estimate';\n      const cls=catOf(primary,s);\n\n      const lbm=w*(1-primary\/100), fatMass=w-lbm;\n      const wDisp=v=>unit==='metric'?num(v,1):num(v\/0.453592,1);\n      const wUnit=unit==='metric'?'kg':'lb';\n\n      let insight;\n      if(cls==='Essential fat') insight={text:'Your estimate falls in the essential fat range. This is quite low and, if unintentional, may be worth discussing with a healthcare professional.',color:'#5B7A93'};\n      else if(cls==='Athletes'||cls==='Fitness') insight={text:'Your estimate falls within the general athletic-to-fitness range for your sex. Maintaining strength training and balanced nutrition can help sustain this.',color:'#1E9E6B'};\n      else if(cls==='Average') insight={text:'Your estimate falls within the general average range. Gradual, sustainable changes in activity and nutrition can shift this over time if that is a goal for you.',color:'#F5A623'};\n      else insight={text:'Your estimate falls in the higher range for your sex. A healthcare professional can help you build a plan that fits your specific situation.',color:'#E0533D'};\n\n      const ffmiVal = lbm\/(hm*hm);\n\n      $('bf-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${primary.toFixed(1)}%<\/div><div class=\"dd-main-sub\">${cls} \u00b7 ${methodLabel}<\/div><\/div>\n        <div class=\"bmi-scale-wrap\">${svgScale(primary,s)}<\/div>\n\n        <div class=\"gpa-insight\" style=\"background:${insight.color}18;border:1px solid ${insight.color}44;\">\n          <b style=\"color:${insight.color};\">Your result:<\/b> ${insight.text}\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('scale',16)}<\/div><h5>Body composition dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Body fat<\/span><b>${primary.toFixed(1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Fat mass<\/span><b>${wDisp(fatMass)} ${wUnit}<\/b><\/div>\n            <div class=\"m-item\"><span>Lean body mass<\/span><b>${wDisp(lbm)} ${wUnit}<\/b><\/div>\n            <div class=\"m-item\"><span>BMI<\/span><b>${bmi.toFixed(1)}<\/b><\/div>\n            <div class=\"m-item\"><span>FFMI<\/span><b>${ffmiVal.toFixed(1)}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Method comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Method<\/th><th>Estimate<\/th><\/tr><\/thead><tbody>\n            <tr><td>BMI-based (Deurenberg)<\/td><td>${bmiEstimate.toFixed(1)}%<\/td><\/tr>\n            <tr><td>U.S. Navy (tape measure)<\/td><td>${navyEstimate!==null?navyEstimate.toFixed(1)+'%':'Add neck\/waist measurements above'}<\/td><\/tr>\n          <\/tbody><\/table>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">The Navy method is generally more accurate than BMI-based estimates since it accounts for body shape, not just weight and height.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Body fat categories (${s==='m'?'men':'women'})<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            ${(s==='m'?CATS_M:CATS_F).map(([lo,hi,label])=>`<tr><td>${label}<\/td><td>${lo}\u2013${hi>=100?'':hi}${hi>=100?'+':'%'}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        ${(+$('bf-target').value)?(()=>{ const target=+$('bf-target').value;\n          const targetW = lbm\/(1-target\/100);\n          const diff = w-targetW;\n          return `<div class=\"calc-section section-navy\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Goal body fat planner<\/h5><\/div>\n            <p style=\"color:var(--text-soft);font-size:.88rem;\">Assuming lean mass stays constant, reaching <b>${target}%<\/b> body fat corresponds to a weight of:<\/p>\n            <div class=\"dd-main-result\" style=\"margin-top:10px;\"><div class=\"dd-main-value\">${wDisp(targetW)} ${wUnit}<\/div><div class=\"dd-main-sub\">${Math.abs(diff)<0.3?'You are already close to this target':(diff>0?`About ${wDisp(diff)} ${wUnit} of fat loss from current weight`:`About ${wDisp(-diff)} ${wUnit} above current weight`)}<\/div><\/div>\n            <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">This is a simplified planning estimate \u2014 actual body recomposition also depends on training, nutrition and individual factors.<\/p>\n          <\/div>`; })():''}\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Integrated energy needs<\/h5><\/div>\n          ${(()=>{ const bmr=370+21.6*lbm; return `<div class=\"summary-mini\"><div class=\"m-item\"><span>BMR (Katch-McArdle, using lean mass)<\/span><b>${num(bmr,0)} cal\/day<\/b><\/div><div class=\"m-item\"><span>Suggested protein target<\/span><b>${num(lbm*2.0,0)}\u2013${num(lbm*2.4,0)}g\/day<\/b><\/div><\/div><p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Protein target based on a common range of ~2.0-2.4g per kg of lean mass, often used to support muscle preservation. See the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for a full daily plan.<\/p>`; })()}\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>What-if: adjust waist measurement<\/h5><\/div>\n          ${navyEstimate!==null ? `<div class=\"field\"><label>Hypothetical waist: <span id=\"bf-sim-val\">${waist.toFixed(0)}<\/span> ${unit==='metric'?'cm':'in'}<\/label><input type=\"range\" id=\"bf-sim-waist\" min=\"${Math.max(waist-20,neck+5)}\" max=\"${waist+20}\" value=\"${waist}\"><\/div><div id=\"bf-sim-result\" class=\"summary-mini\"><\/div>` : `<p style=\"color:var(--text-soft);font-size:.88rem;\">Add neck and waist measurements above to enable this simulator.<\/p>`}\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"bf-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"bf-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Your measurements are calculated locally in your browser and are not stored. These are estimates, not medical diagnoses.<\/p>`;\n\n      if(navyEstimate!==null){\n        const simSlider=$('bf-sim-waist');\n        function simUpdate(){\n          const simWaist=+simSlider.value;\n          $('bf-sim-val').textContent=simWaist.toFixed(0);\n          try{\n            const simBF=navyBF(s,h,neck,simWaist,hip);\n            $('bf-sim-result').innerHTML = `<div class=\"m-item\"><span>Simulated body fat<\/span><b>${simBF.toFixed(1)}%<\/b><\/div><div class=\"m-item\"><span>Category<\/span><b>${catOf(simBF,s)}<\/b><\/div><div class=\"m-item\"><span>Change<\/span><b>${simBF>primary?'+':''}${(simBF-primary).toFixed(1)}%<\/b><\/div>`;\n          }catch(e){ $('bf-sim-result').innerHTML='<p style=\"font-size:.85rem;color:var(--text-soft);\">Adjust within a valid range.<\/p>'; }\n        }\n        simSlider.oninput=simUpdate; simUpdate();\n      }\n\n      const shareText=`My estimated body fat is ${primary.toFixed(1)}% (${cls}, ${methodLabel}). Lean mass: ${wDisp(lbm)}${wUnit}. Calculated on MAFtrix.`;\n      $('bf-copy').onclick=()=>{ const done=()=>{$('bf-copy').textContent='Copied!';setTimeout(()=>$('bf-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('bf-print').onclick=()=>window.print();\n    };\n    $('bf-btn').click();\n  }});\n\nreg({slug:'ideal-weight',name:'Ideal Weight Calculator',cat:'health',icn:'scale',photo:34,\n  desc:'A complete healthy-weight hub comparing 6 established formulas, your BMI-based healthy range, frame-size adjustment, goal planning and a weight timeline.',\n  render(el){\n    function formulas(sex,heightCm){\n      const inches=heightCm\/2.54, over5ft=Math.max(0,inches-60), hm=heightCm\/100;\n      return {\n        Devine: sex==='m'?50+2.3*over5ft:45.5+2.3*over5ft,\n        Robinson: sex==='m'?52+1.9*over5ft:49+1.7*over5ft,\n        Miller: sex==='m'?56.2+1.41*over5ft:53.1+1.36*over5ft,\n        Hamwi: sex==='m'?48+2.7*over5ft:45.5+2.2*over5ft,\n        Lorentz: sex==='m'?heightCm-100-((heightCm-150)\/4):heightCm-100-((heightCm-150)\/2.5),\n        Peterson: 2.2*22+3.5*22*(hm-1.5)\n      };\n    }\n    const FRAME_ADJ={small:-0.10,medium:0,large:0.10};\n\n    el.innerHTML = `\n      <div class=\"iw-shell\">\n      <div class=\"seg\" id=\"iw-unit-seg\"><button class=\"active\" data-v=\"metric\">Metric (kg\/cm)<\/button><button data-v=\"imperial\">Imperial (lb\/in)<\/button><\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Sex<\/label><select id=\"iw-s\"><option value=\"m\">Male<\/option><option value=\"f\">Female<\/option><\/select><\/div>\n        <div class=\"field\"><label>Age<\/label><input type=\"number\" id=\"iw-age\" value=\"30\"><\/div>\n      <\/div>\n      <div class=\"form-row\">\n        <div class=\"field\"><label id=\"iw-h-label\">Height (cm)<\/label><input type=\"number\" id=\"iw-h\" value=\"170\"><\/div>\n        <div class=\"field\"><label id=\"iw-w-label\">Current weight (kg)<\/label><input type=\"number\" id=\"iw-w\" value=\"75\"><\/div>\n      <\/div>\n      <div class=\"field\"><label>Body frame size<\/label><select id=\"iw-frame\"><option value=\"medium\" selected>Medium<\/option><option value=\"small\">Small<\/option><option value=\"large\">Large<\/option><\/select><\/div>\n      <span class=\"more-link\" id=\"iw-more-link\">+ Body fat % &amp; weight goal<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"iw-more-panel\">\n        <div class=\"field\"><label>Body fat % <small>(optional)<\/small><\/label><input type=\"number\" id=\"iw-bf\" placeholder=\"e.g. 22\"><\/div>\n        <div class=\"section-tag\">Weight goal<\/div>\n        <div class=\"field\"><label>Target<\/label>\n          <select id=\"iw-goal-type\">\n            <option value=\"mid\">Middle of healthy BMI range<\/option>\n            <option value=\"avg\">Average of all formulas<\/option>\n            <option value=\"Devine\">Devine formula<\/option>\n            <option value=\"Robinson\">Robinson formula<\/option>\n            <option value=\"Miller\">Miller formula<\/option>\n            <option value=\"Hamwi\">Hamwi formula<\/option>\n          <\/select>\n        <\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"iw-btn\">Calculate<\/button>\n\n      <div id=\"iw-results\"><\/div>\n      <\/div>`;\n\n    let unit='metric';\n    $('iw-unit-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('iw-unit-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active'); unit=b.dataset.v;\n      $('iw-h-label').textContent=unit==='metric'?'Height (cm)':'Height (in)';\n      $('iw-w-label').textContent=unit==='metric'?'Current weight (kg)':'Current weight (lb)';\n    });\n    $('iw-more-link').onclick=()=>$('iw-more-panel').classList.toggle('show');\n\n    $('iw-btn').onclick=()=>{\n      const sex=$('iw-s').value, age=+$('iw-age').value;\n      let h=+$('iw-h').value, w=+$('iw-w').value;\n      const bf=+$('iw-bf').value||0, frame=$('iw-frame').value;\n      if(unit==='imperial'){ h*=2.54; w*=0.453592; }\n      if(h<=0||w<=0){ $('iw-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Enter a valid height and weight.<\/div><\/div>'; return; }\n\n      const f=formulas(sex,h);\n      const adj=FRAME_ADJ[frame];\n      const fAdj={}; Object.keys(f).forEach(k=>fAdj[k]=f[k]*(1+adj));\n      const avg=Object.values(fAdj).reduce((a,b)=>a+b,0)\/Object.keys(fAdj).length;\n\n      const hm=h\/100, bmi=w\/(hm*hm);\n      const lowW=18.5*hm*hm, highW=24.9*hm*hm, midW=(lowW+highW)\/2;\n      const wDisp=v=>unit==='metric'?num(v,1):num(v\/0.453592,1);\n      const wUnit=unit==='metric'?'kg':'lb';\n\n      const goalType=$('iw-goal-type').value;\n      const targetW = goalType==='mid'?midW : goalType==='avg'?avg : fAdj[goalType];\n      const diff=w-targetW;\n      const bmr=sex==='m'?10*w+6.25*h-5*age+5:10*w+6.25*h-5*age-161;\n      const maint=bmr*1.4;\n\n      $('iw-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${wDisp(avg)} ${wUnit}<\/div><div class=\"dd-main-sub\">Average ideal weight across 6 formulas (${frame} frame)<\/div><\/div>\n\n        <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;\">\n          <b style=\"color:var(--teal-dark);\">Context:<\/b> Formulas estimate between ${wDisp(Math.min(...Object.values(fAdj)))} and ${wDisp(Math.max(...Object.values(fAdj)))} ${wUnit} \u2014 ideal weight is best viewed as a range, not one exact number.\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Formula comparison<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Formula<\/th><th>Estimate<\/th><\/tr><\/thead><tbody>\n            ${Object.entries(fAdj).map(([name,v])=>`<tr><td>${name}<\/td><td>${wDisp(v)} ${wUnit}<\/td><\/tr>`).join('')}\n            <tr style=\"font-weight:700;\"><td>Average<\/td><td>${wDisp(avg)} ${wUnit}<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Weight distribution at a glance<\/h5><\/div>\n          <div id=\"iw-dist-viz\"><\/div>\n          <div class=\"iw-dist-legend\">\n            <span><span class=\"sw\" style=\"background:rgba(30,158,107,.6);\"><\/span>Healthy BMI range<\/span>\n            <span><span class=\"sw\" style=\"background:var(--teal-dark);\"><\/span>Formula estimates<\/span>\n            <span><span class=\"sw\" style=\"background:var(--ink);\"><\/span>Current weight<\/span>\n            <span><span class=\"sw\" style=\"background:var(--amber);\"><\/span>Goal target<\/span>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('scale',16)}<\/div><h5>BMI &amp; healthy weight range<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Current BMI<\/span><b>${bmi.toFixed(1)}<\/b><\/div>\n            <div class=\"m-item\"><span>Current weight<\/span><b>${wDisp(w)} ${wUnit}<\/b><\/div>\n            <div class=\"m-item\"><span>Healthy range<\/span><b>${wDisp(lowW)}\u2013${wDisp(highW)} ${wUnit}<\/b><\/div>\n            <div class=\"m-item\"><span>Difference from range<\/span><b>${w<lowW?wDisp(lowW-w)+' below':w>highW?wDisp(w-highW)+' above':'Within range'}<\/b><\/div>\n          <\/div>\n          <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">See the <a href=\"#\/calculator\/bmi\">BMI Calculator<\/a> for a full breakdown.<\/p>\n        <\/div>\n\n        ${bf>0?(()=>{ const lbm=w*(1-bf\/100), fatMass=w-lbm;\n          return `<div class=\"calc-section section-amber\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Body composition<\/h5><\/div>\n            <div class=\"summary-mini\"><div class=\"m-item\"><span>Lean body mass<\/span><b>${wDisp(lbm)} ${wUnit}<\/b><\/div><div class=\"m-item\"><span>Fat mass<\/span><b>${wDisp(fatMass)} ${wUnit}<\/b><\/div><\/div>\n            <p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">Two people at the same weight can have very different body composition \u2014 see the <a href=\"#\/calculator\/body-fat\">Body Fat Calculator<\/a> for more detail.<\/p>\n          <\/div>`; })():''}\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Weight goal planner<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">Target: <b>${goalType==='mid'?'Middle of healthy BMI range':goalType==='avg'?'Average of all formulas':goalType+' formula'}<\/b><\/p>\n          <div class=\"dd-main-result\" style=\"margin-top:8px;\"><div class=\"dd-main-value\">${wDisp(targetW)} ${wUnit}<\/div><div class=\"dd-main-sub\">${Math.abs(diff)<0.5?'You are already close to this target':(diff>0?`${wDisp(diff)} ${wUnit} to lose (${num(Math.abs(diff)\/w*100,1)}% change)`:`${wDisp(-diff)} ${wUnit} to gain (${num(Math.abs(diff)\/w*100,1)}% change)`)}<\/div><\/div>\n          ${Math.abs(diff)>=0.5?(()=>{ const weeklyKg=(500*7)\/7700; const diffKg=unit==='metric'?Math.abs(diff):Math.abs(diff)*0.453592; const weeks=diffKg\/weeklyKg;\n            return `<p style=\"font-size:.78rem;color:var(--text-soft);margin-top:10px;\">At a moderate ~500 cal\/day pace (\u2248${wDisp(weeklyKg)} ${wUnit}\/week), reaching this target would take roughly <b>${num(weeks,0)} weeks<\/b>. This is an estimate, not a guarantee.<\/p>`; })():''}\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Estimated daily calories<\/h5><\/div>\n          <div class=\"summary-mini\"><div class=\"m-item\"><span>Maintenance (approx.)<\/span><b>${num(maint,0)} cal\/day<\/b><\/div><div class=\"m-item\"><span>Gradual loss<\/span><b>${num(maint-500,0)} cal\/day<\/b><\/div><div class=\"m-item\"><span>Gradual gain<\/span><b>${num(maint+500,0)} cal\/day<\/b><\/div><\/div><p style=\"font-size:.78rem;color:var(--text-soft);margin-top:8px;\">See the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for a full nutrition plan.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Macro recommendations<\/h5><\/div>\n          <div class=\"field\" style=\"margin-bottom:12px;\"><label>Style<\/label><select id=\"iw-macro-style\"><option value=\"balanced\">Balanced<\/option><option value=\"highprotein\">Higher protein<\/option><option value=\"lowcarb\">Lower carbohydrate<\/option><\/select><\/div>\n          <div id=\"iw-macro-table\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Goal planning scenarios<\/h5><\/div>\n          <div id=\"iw-scenarios\"><\/div>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">All timelines are estimates based on a common ~7,700 kcal \u2248 1 kg approximation \u2014 individual results vary.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>What-if: adjust weight<\/h5><\/div>\n          <div class=\"field\"><label>Hypothetical weight: <span id=\"iw-sim-val\">${wDisp(w)}<\/span> ${wUnit}<\/label><input type=\"range\" id=\"iw-sim-w\" min=\"${wDisp(Math.max(30,w*0.6))}\" max=\"${wDisp(w*1.4)}\" value=\"${wDisp(w)}\" step=\"0.5\"><\/div>\n          <div id=\"iw-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"iw-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"iw-print\" type=\"button\">Print<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Your information is calculated locally in your browser and is not stored. Ideal weight formulas are estimates \u2014 individual healthy weight varies with body composition, frame, and other factors.<\/p>`;\n\n      const simSlider=$('iw-sim-w');\n      function simUpdate(){\n        const simWraw=+simSlider.value;\n        $('iw-sim-val').textContent=simWraw;\n        const simW = unit==='metric'?simWraw:simWraw*0.453592;\n        const simBMI=simW\/(hm*hm);\n        const simBMR=sex==='m'?10*simW+6.25*h-5*age+5:10*simW+6.25*h-5*age-161;\n        $('iw-sim-result').innerHTML = `<div class=\"m-item\"><span>Simulated BMI<\/span><b>${simBMI.toFixed(1)}<\/b><\/div><div class=\"m-item\"><span>Position<\/span><b>${simW<lowW?'Below healthy range':simW>highW?'Above healthy range':'Within healthy range'}<\/b><\/div><div class=\"m-item\"><span>Est. maintenance calories<\/span><b>${num(simBMR*1.4,0)} cal\/day<\/b><\/div>`;\n      }\n      simSlider.oninput=simUpdate; simUpdate();\n\n      \/\/ weight distribution visualization\n      (function renderDist(){\n        const allVals=[...Object.values(fAdj), w, targetW, lowW, highW];\n        const min=Math.min(...allVals)*0.92, max=Math.max(...allVals)*1.08, span=max-min;\n        const xOf=v=>((v-min)\/span)*100;\n        let html=`<div class=\"iw-dist-wrap\">`;\n        html+=`<div class=\"track\"><\/div>`;\n        html+=`<div class=\"range-band\" style=\"left:${xOf(lowW)}%;width:${xOf(highW)-xOf(lowW)}%;\"><\/div>`;\n        Object.values(fAdj).forEach(v=>{ html+=`<div class=\"fpt\" style=\"left:${xOf(v)}%;\"><\/div>`; });\n        html+=`<div class=\"cpt\" style=\"left:${xOf(w)}%;\"><\/div><div class=\"dlabel\" style=\"left:${xOf(w)}%;top:6px;\">Current ${wDisp(w)}<\/div>`;\n        html+=`<div class=\"tpt\" style=\"left:${xOf(targetW)}%;\"><\/div><div class=\"dlabel\" style=\"left:${xOf(targetW)}%;\">Target ${wDisp(targetW)}<\/div>`;\n        html+=`<\/div>`;\n        $('iw-dist-viz').innerHTML=html;\n      })();\n\n      \/\/ macro recommendations\n      const MACRO_STYLES_IW={balanced:{p:0.3,c:0.4,f:0.3},highprotein:{p:0.4,c:0.3,f:0.3},lowcarb:{p:0.35,c:0.25,f:0.4}};\n      function renderIWMacros(){\n        const style=MACRO_STYLES_IW[$('iw-macro-style').value];\n        const pG=(maint*style.p)\/4, cG=(maint*style.c)\/4, fG=(maint*style.f)\/9;\n        $('iw-macro-table').innerHTML = `<p style=\"color:var(--text-soft);font-size:.85rem;\">At ${num(maint,0)} maintenance cal\/day:<\/p><div class=\"summary-mini\"><div class=\"m-item\"><span>Protein<\/span><b>${num(pG,0)}g<\/b><\/div><div class=\"m-item\"><span>Carbohydrates<\/span><b>${num(cG,0)}g<\/b><\/div><div class=\"m-item\"><span>Fat<\/span><b>${num(fG,0)}g<\/b><\/div><\/div>`;\n      }\n      $('iw-macro-style').onchange=renderIWMacros; renderIWMacros();\n\n      \/\/ goal scenarios comparison\n      (function renderScenarios(){\n        const scenarios=[['Gradual loss',-500],['Maintain',0],['Gradual gain',500]];\n        const rows=scenarios.map(([label,delta])=>{\n          const cals=maint+delta;\n          let timeline='\u2014';\n          if(delta!==0){\n            const weeklyKg=(Math.abs(delta)*7)\/7700;\n            const diffKg=unit==='metric'?Math.abs(diff):Math.abs(diff)*0.453592;\n            const weeks=diffKg>0.5 ? diffKg\/weeklyKg : 0;\n            timeline = weeks>0 ? `\u2248${num(weeks,0)} weeks to target` : '\u2014';\n          }\n          return `<tr><td>${label}<\/td><td>${num(cals,0)} cal\/day<\/td><td>${timeline}<\/td><\/tr>`;\n        }).join('');\n        $('iw-scenarios').innerHTML = `<table class=\"ref-table\"><thead><tr><th>Scenario<\/th><th>Calories\/day<\/th><th>Timeline to target<\/th><\/tr><\/thead><tbody>${rows}<\/tbody><\/table>`;\n      })();\n\n      const shareText=`My average ideal weight estimate is ${wDisp(avg)} ${wUnit} across 6 formulas (current: ${wDisp(w)} ${wUnit}, BMI ${bmi.toFixed(1)}). Calculated on MAFtrix.`;\n      $('iw-copy').onclick=()=>{ const done=()=>{$('iw-copy').textContent='Copied!';setTimeout(()=>$('iw-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('iw-print').onclick=()=>window.print();\n    };\n    $('iw-btn').click();\n  }});\n\nreg({slug:'pregnancy',name:'Pregnancy Due Date Calculator',cat:'health',icn:'baby',photo:35,\n  desc:'A complete pregnancy timeline: due date by LMP, conception, IVF or ultrasound, weekly fetal size, milestone windows, weight gain guide, checklist, kick counter and contraction timer.',\n  render(el){\n    let kickTimer=null, kickCount=0, kickStart=null;\n    let contractions=[];\n\n    const FETAL_SIZE=[\n      [4,'\ud83c\udf31','a poppy seed'],[5,'\ud83c\udf31','a sesame seed'],[6,'\ud83e\uded8','a lentil'],[7,'\ud83e\uded0','a blueberry'],[8,'\ud83c\udf47','a raspberry'],\n      [9,'\ud83c\udf47','a grape'],[10,'\ud83c\udf4a','a kumquat'],[11,'\ud83e\uded2','a fig'],[12,'\ud83c\udf4b','a lime'],[13,'\ud83c\udf4b','a lemon'],\n      [14,'\ud83c\udf51','a peach'],[15,'\ud83c\udf4e','an apple'],[16,'\ud83e\udd51','an avocado'],[17,'\ud83e\udd54','a turnip'],[18,'\ud83e\uded1','a bell pepper'],\n      [19,'\ud83c\udf45','a tomato'],[20,'\ud83c\udf4c','a banana'],[21,'\ud83e\udd55','a carrot'],[22,'\ud83e\udd6d','a papaya'],[23,'\ud83c\udf48','a grapefruit'],\n      [24,'\ud83c\udf3d','an ear of corn'],[25,'\ud83e\udd6c','a rutabaga'],[26,'\ud83e\uddc5','a scallion bunch'],[27,'\ud83e\udd66','a cauliflower'],[28,'\ud83c\udf46','an eggplant'],\n      [29,'\ud83c\udf83','a butternut squash'],[30,'\ud83e\udd6c','a cabbage'],[31,'\ud83e\udd65','a coconut'],[32,'\ud83e\udd54','a jicama'],[33,'\ud83c\udf4d','a pineapple'],\n      [34,'\ud83c\udf48','a cantaloupe'],[35,'\ud83c\udf48','a honeydew melon'],[36,'\ud83e\udd6c','a head of romaine'],[37,'\ud83c\udf3f','a bunch of chard'],[38,'\ud83e\udd6c','a leek bunch'],\n      [39,'\ud83c\udf49','a mini watermelon'],[40,'\ud83c\udf83','a small pumpkin'],[41,'\ud83c\udf49','a watermelon'],[42,'\ud83c\udf49','a large watermelon']\n    ];\n    function fetalSizeFor(week){\n      const w=Math.max(4,Math.min(42,week));\n      let entry=FETAL_SIZE[0];\n      for(const e of FETAL_SIZE){ if(e[0]<=w) entry=e; }\n      return entry;\n    }\n    const TRIMESTER_INFO={\n      First:{\n        body:'Common early changes may include breast tenderness, fatigue, and in some cases spotting \u2014 experiences vary widely between individuals.',\n        symptoms:['Fatigue','Nausea (sometimes called morning sickness, though it can occur any time of day)','Breast tenderness','Frequent urination'],\n        tips:['Stay hydrated throughout the day','Consider small, frequent meals if nausea is a factor','Schedule your first prenatal visit if you have not already','Begin prenatal vitamins as recommended by your provider','Prioritize rest \u2014 fatigue is common in this stage']\n      },\n      Second:{\n        body:'Many people describe this stage as more comfortable; a visible bump commonly becomes noticeable, and fetal movement is often first felt during this window.',\n        symptoms:['Round ligament pain','Back discomfort','Skin changes for some','Increased appetite'],\n        tips:['Discuss the anatomy scan schedule with your provider','Consider gentle, provider-approved physical activity','Continue balanced nutrition and hydration','Track fetal movement patterns as they become noticeable']\n      },\n      Third:{\n        body:'As space becomes more limited, physical discomfort is common; Braxton Hicks (practice) contractions are frequently reported by many during this stage.',\n        symptoms:['Braxton Hicks contractions','Back and pelvic discomfort','Difficulty sleeping','Shortness of breath for some'],\n        tips:['Discuss your birth plan with your provider','Pack a hospital bag ahead of your due date','Rest when possible and stay hydrated','Know your provider guidance on when to call or go to the hospital']\n      }\n    };\n    const MILESTONES=[\n      [42,'Heartbeat may become detectable by ultrasound'],\n      [56,'First prenatal visit is commonly scheduled around now'],\n      [77,'Nuchal translucency screening window often begins'],\n      [126,'Anatomy scan window often begins'],\n      [168,'Glucose screening window often begins'],\n      [168,'Commonly referenced viability milestone'],\n      [259,'Full-term milestone (37 weeks)']\n    ];\n\n    function fmtLong(d){ return d.toLocaleDateString('en-US',{weekday:'long',year:'numeric',month:'long',day:'numeric'}); }\n    function fmtShort(d){ return d.toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'}); }\n\n    el.innerHTML = `\n      <div class=\"pg-shell\">\n      <div class=\"privacy-note\" style=\"margin-top:0;margin-bottom:16px;\">\u26a0 This tool provides general estimates for informational purposes only and is not a substitute for medical advice. Always follow your healthcare provider's guidance for your specific pregnancy.<\/div>\n\n      <div class=\"seg\" id=\"pg-mode-seg\">\n        <button class=\"active\" data-v=\"lmp\">Last period (LMP)<\/button>\n        <button data-v=\"conception\">Conception date<\/button>\n        <button data-v=\"ivf\">IVF transfer<\/button>\n        <button data-v=\"ultrasound\">Ultrasound<\/button>\n      <\/div>\n\n      <div id=\"pg-lmp-fields\">\n        <div class=\"field\"><label>First day of last menstrual period<\/label><input type=\"date\" id=\"pg-lmp-date\"><\/div>\n        <div class=\"field\"><label>Average cycle length (days)<\/label><input type=\"number\" id=\"pg-cycle\" value=\"28\" min=\"21\" max=\"45\"><\/div>\n      <\/div>\n      <div id=\"pg-conception-fields\" hidden>\n        <div class=\"field\"><label>Estimated conception date<\/label><input type=\"date\" id=\"pg-conception-date\"><\/div>\n      <\/div>\n      <div id=\"pg-ivf-fields\" hidden>\n        <div class=\"field\"><label>Embryo transfer date<\/label><input type=\"date\" id=\"pg-ivf-date\"><\/div>\n        <div class=\"field\"><label>Embryo age at transfer<\/label><select id=\"pg-ivf-age\"><option value=\"5\">5-day embryo (blastocyst)<\/option><option value=\"3\">3-day embryo<\/option><\/select><\/div>\n      <\/div>\n      <div id=\"pg-ultrasound-fields\" hidden>\n        <div class=\"field\"><label>Ultrasound date<\/label><input type=\"date\" id=\"pg-us-date\"><\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Gestational age at ultrasound \u2014 weeks<\/label><input type=\"number\" id=\"pg-us-weeks\" value=\"8\" min=\"0\" max=\"42\"><\/div>\n          <div class=\"field\"><label>+ days<\/label><input type=\"number\" id=\"pg-us-days\" value=\"0\" min=\"0\" max=\"6\"><\/div>\n        <\/div>\n      <\/div>\n\n      <span class=\"more-link\" id=\"pg-more-link\">+ Pre-pregnancy weight gain guide<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"pg-more-panel\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Pre-pregnancy height (cm)<\/label><input type=\"number\" id=\"pg-height\" placeholder=\"e.g. 165\"><\/div>\n          <div class=\"field\"><label>Pre-pregnancy weight (kg)<\/label><input type=\"number\" id=\"pg-preweight\" placeholder=\"e.g. 62\"><\/div>\n        <\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"pg-btn\">Calculate due date<\/button>\n\n      <div id=\"pg-results\"><\/div>\n      <\/div>`;\n\n    let mode='lmp';\n    $('pg-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('pg-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('pg-lmp-fields').hidden=mode!=='lmp';\n      $('pg-conception-fields').hidden=mode!=='conception';\n      $('pg-ivf-fields').hidden=mode!=='ivf';\n      $('pg-ultrasound-fields').hidden=mode!=='ultrasound';\n    });\n    $('pg-more-link').onclick=()=>$('pg-more-panel').classList.toggle('show');\n\n    $('pg-btn').onclick=()=>{\n      let effectiveLMP, dueDate, conceptionDate;\n      if(mode==='lmp'){\n        const v=$('pg-lmp-date').value;\n        if(!v){ $('pg-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Please select the first day of your last period.<\/div><\/div>'; return; }\n        const cycle=+$('pg-cycle').value||28;\n        effectiveLMP=new Date(v);\n        dueDate=new Date(effectiveLMP.getTime()+(280+(cycle-28))*86400000);\n        conceptionDate=new Date(effectiveLMP.getTime()+14*86400000);\n      } else if(mode==='conception'){\n        const v=$('pg-conception-date').value;\n        if(!v){ $('pg-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Please select the estimated conception date.<\/div><\/div>'; return; }\n        conceptionDate=new Date(v);\n        effectiveLMP=new Date(conceptionDate.getTime()-14*86400000);\n        dueDate=new Date(conceptionDate.getTime()+266*86400000);\n      } else if(mode==='ivf'){\n        const v=$('pg-ivf-date').value;\n        if(!v){ $('pg-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Please select the embryo transfer date.<\/div><\/div>'; return; }\n        const embryoAge=+$('pg-ivf-age').value;\n        const transferDate=new Date(v);\n        const offset=embryoAge===5?261:263;\n        dueDate=new Date(transferDate.getTime()+offset*86400000);\n        effectiveLMP=new Date(dueDate.getTime()-280*86400000);\n        conceptionDate=new Date(transferDate.getTime()-embryoAge*86400000);\n      } else {\n        const v=$('pg-us-date').value;\n        if(!v){ $('pg-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your input<\/div><div class=\"r-value\">Please select the ultrasound date.<\/div><\/div>'; return; }\n        const gaWeeks=+$('pg-us-weeks').value||0, gaDays=+$('pg-us-days').value||0;\n        const usDate=new Date(v);\n        effectiveLMP=new Date(usDate.getTime()-(gaWeeks*7+gaDays)*86400000);\n        dueDate=new Date(effectiveLMP.getTime()+280*86400000);\n        conceptionDate=new Date(effectiveLMP.getTime()+14*86400000);\n      }\n\n      const today=new Date();\n      const gaDaysTotal=Math.floor((today-effectiveLMP)\/86400000);\n      const gaWeeksNow=Math.max(0,Math.floor(gaDaysTotal\/7)), gaDaysRem=Math.max(0,gaDaysTotal%7);\n      const daysRemaining=Math.floor((dueDate-today)\/86400000);\n      const trimester = gaWeeksNow<14?'First':gaWeeksNow<28?'Second':'Third';\n      const t2start=new Date(effectiveLMP.getTime()+14*7*86400000);\n      const t3start=new Date(effectiveLMP.getTime()+28*7*86400000);\n      const fruit=fetalSizeFor(gaWeeksNow);\n      const isPastDue = daysRemaining<0;\n\n      $('pg-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${fmtLong(dueDate)}<\/div><div class=\"dd-main-sub\">Estimated due date \u00b7 ${isPastDue?'Past estimated due date':daysRemaining+' days remaining'}<\/div><\/div>\n\n        <div class=\"gpa-insight\" style=\"background:#0EA5A018;border:1px solid #0EA5A044;\">\n          <b style=\"color:var(--teal-dark);\">You are currently in your ${trimester.toLowerCase()} trimester<\/b> \u2014 week ${gaWeeksNow}, day ${gaDaysRem}. Every pregnancy is unique, so timelines and experiences vary.\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('baby',16)}<\/div><h5>Pregnancy dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Current week<\/span><b>${gaWeeksNow}w ${gaDaysRem}d<\/b><\/div>\n            <div class=\"m-item\"><span>Trimester<\/span><b>${trimester}<\/b><\/div>\n            <div class=\"m-item\"><span>Days remaining<\/span><b>${isPastDue?'Past due':daysRemaining}<\/b><\/div>\n            <div class=\"m-item\"><span>Weeks completed<\/span><b>${gaWeeksNow}<\/b><\/div>\n            <div class=\"m-item\"><span>Estimated conception<\/span><b>${fmtShort(conceptionDate)}<\/b><\/div>\n            <div class=\"m-item\"><span>Months completed (approx.)<\/span><b>${(gaWeeksNow\/4.345).toFixed(1)}<\/b><\/div>\n          <\/div>\n          <div style=\"margin-top:16px;\">\n            <div style=\"display:flex;justify-content:space-between;font-size:.8rem;color:var(--text-soft);margin-bottom:6px;\"><span>Trimester progress<\/span><span>${Math.min(100,(gaWeeksNow\/40*100)).toFixed(0)}% through pregnancy<\/span><\/div>\n            <div class=\"gpa-progress\"><div class=\"gpa-progress-fill\" style=\"width:${Math.min(100,gaWeeksNow\/40*100)}%;\"><\/div><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('calendar',16)}<\/div><h5>Key dates<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            <tr><td>Estimated conception<\/td><td>${fmtShort(conceptionDate)}<\/td><\/tr>\n            <tr><td>Second trimester begins (week 14)<\/td><td>${fmtShort(t2start)}<\/td><\/tr>\n            <tr><td>Third trimester begins (week 28)<\/td><td>${fmtShort(t3start)}<\/td><\/tr>\n            <tr><td>Estimated due date (week 40)<\/td><td>${fmtShort(dueDate)}<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>Timeline<\/h5><\/div>\n          <div class=\"pg-timeline\" id=\"pg-timeline-el\"><\/div>\n        <\/div>\n\n        ${gaWeeksNow>=4?`\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('baby',16)}<\/div><h5>This week<\/h5><\/div>\n          <div class=\"pg-fruit-box\"><span class=\"emoji\">${fruit[1]}<\/span><div><b>Week ${gaWeeksNow}:<\/b> baby is roughly the size of ${fruit[2]} (approximate, for general reference only).<\/div><\/div>\n          <p style=\"font-size:.85rem;color:var(--text-soft);margin-top:14px;\"><b>Mother's body (general):<\/b> ${TRIMESTER_INFO[trimester].body}<\/p>\n          <p style=\"font-size:.85rem;color:var(--text-soft);margin-top:10px;\"><b>Commonly reported symptoms:<\/b> ${TRIMESTER_INFO[trimester].symptoms.join(', ')}. Every pregnancy is different \u2014 these are commonly reported, not universal.<\/p>\n          <p style=\"font-size:.85rem;color:var(--text-soft);margin-top:10px;\"><b>General tips:<\/b> ${TRIMESTER_INFO[trimester].tips.join(' \u2022 ')}<\/p>\n        <\/div>`:''}\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>General nutrition summary<\/h5><\/div>\n          ${(()=>{ const calAdd = trimester==='First'?0:trimester==='Second'?340:450;\n            return `<div class=\"summary-mini\"><div class=\"m-item\"><span>Estimated added calories\/day<\/span><b>+${calAdd}<\/b><\/div><div class=\"m-item\"><span>General protein guidance<\/span><b>\u224871g\/day<\/b><\/div><div class=\"m-item\"><span>General water guidance<\/span><b>\u22482.3L\/day<\/b><\/div><\/div>`; })()}\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">General published reference figures, not individualized advice. See the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> and cross-reference with your provider for personal guidance.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>General milestone windows<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Milestone<\/th><th>Approx. date<\/th><\/tr><\/thead><tbody>\n            ${MILESTONES.map(([days,label])=>`<tr><td>${label}<\/td><td>${fmtShort(new Date(effectiveLMP.getTime()+days*86400000))}<\/td><\/tr>`).join('')}\n          <\/tbody><\/table>\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">\u26a0 These are general windows only \u2014 your actual schedule depends on your healthcare provider's recommendations and individual circumstances.<\/p>\n        <\/div>\n\n        ${(+$('pg-height').value && +$('pg-preweight').value)?(()=>{\n          const h=+$('pg-height').value\/100, w=+$('pg-preweight').value, bmi=w\/(h*h);\n          let range, cat;\n          if(bmi<18.5){ range='12.5\u201318 kg (28\u201340 lb)'; cat='Underweight'; }\n          else if(bmi<25){ range='11.5\u201316 kg (25\u201335 lb)'; cat='Healthy weight'; }\n          else if(bmi<30){ range='7\u201311.5 kg (15\u201325 lb)'; cat='Overweight'; }\n          else { range='5\u20139 kg (11\u201320 lb)'; cat='Obesity range'; }\n          return `<div class=\"calc-section section-green\">\n            <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('scale',16)}<\/div><h5>General weight gain guide<\/h5><\/div>\n            <div class=\"summary-mini\"><div class=\"m-item\"><span>Pre-pregnancy BMI<\/span><b>${bmi.toFixed(1)}<\/b><\/div><div class=\"m-item\"><span>Category<\/span><b>${cat}<\/b><\/div><div class=\"m-item\"><span>General guideline range<\/span><b>${range}<\/b><\/div><\/div>\n            <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:8px;\">Based on general published guidelines (single pregnancy). Your healthcare provider's individual recommendation for you takes priority over this general reference.<\/p>\n          <\/div>`; })():''}\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('grad',16)}<\/div><h5>Pregnancy checklist<\/h5><\/div>\n          <div id=\"pg-checklist\"><\/div>\n          <p style=\"font-size:.72rem;color:var(--text-soft);margin-top:10px;\">Checklist state is kept only for this browser session and is not saved permanently.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Hospital bag checklist<\/h5><\/div>\n          <div id=\"pg-hospital-checklist\"><\/div>\n          <button class=\"btn btn-ghost btn-sm\" id=\"pg-hospital-print\" type=\"button\" style=\"margin-top:12px;\">Print this checklist<\/button>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('grad',16)}<\/div><h5>Baby name planner<\/h5><\/div>\n          <div class=\"form-row\">\n            <input type=\"text\" id=\"pg-name-input\" placeholder=\"Add a name you like\">\n            <button class=\"btn btn-primary btn-sm\" id=\"pg-name-add\" type=\"button\">Add<\/button>\n          <\/div>\n          <div id=\"pg-name-list\" style=\"margin-top:12px;\"><\/div>\n          <p style=\"font-size:.72rem;color:var(--text-soft);margin-top:10px;\">Saved only for this browser session.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Kick counter<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.85rem;\">Tap the button each time you feel a movement. If you notice a significant change in your baby's movement patterns, contact your healthcare provider promptly.<\/p>\n          <div class=\"pg-timer-display\" id=\"pg-kick-display\">0 kicks \u00b7 00:00<\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-primary btn-block\" id=\"pg-kick-tap\" type=\"button\">Tap for each kick<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"pg-kick-reset\" type=\"button\">Reset<\/button>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Contraction timer<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.85rem;\">Tap start when a contraction begins, stop when it ends. Many providers reference a \"5-1-1\" pattern (contractions 5 minutes apart, lasting 1 minute, for 1 hour) as a general guide for when to call \u2014 always follow your own provider's specific instructions.<\/p>\n          <div class=\"form-row\">\n            <button class=\"btn btn-primary btn-block\" id=\"pg-contraction-btn\" type=\"button\">Start contraction<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"pg-contraction-clear\" type=\"button\">Clear log<\/button>\n          <\/div>\n          <div id=\"pg-contraction-log\" style=\"margin-top:14px;\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"pg-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"pg-print\" type=\"button\">Print<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"pg-ics\" type=\"button\">Add to calendar (.ics)<\/button>\n          <\/div>\n        <\/div>\n        <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 Your information is calculated locally in your browser and is not stored. This tool provides general estimates only and does not replace professional prenatal care.<\/p>`;\n\n      \/\/ timeline\n      const weeks=[1,12,20,28,36,40];\n      let tHtml='<div class=\"track\"><\/div><div class=\"fill\" style=\"left:0;width:100%;\"><\/div>';\n      weeks.forEach(wk=>{\n        const pct=wk\/40*100;\n        tHtml+=`<div class=\"pt\" style=\"left:${pct}%;\"><\/div><div class=\"pt-label\" style=\"left:${pct}%;\">Wk ${wk}<\/div>`;\n      });\n      const curPct=Math.min(100,gaWeeksNow\/40*100);\n      tHtml+=`<div class=\"pt-label bottom\" style=\"left:${curPct}%;color:var(--teal-dark);font-weight:700;\">You: Wk ${gaWeeksNow}<\/div>`;\n      $('pg-timeline-el').innerHTML=tHtml;\n\n      \/\/ checklist\n      const CHECKLIST={\n        'First trimester':['Schedule first prenatal visit','Begin prenatal vitamins (as recommended by your provider)','Discuss current medications with your healthcare provider'],\n        'Second trimester':['Anatomy scan','Glucose screening (if recommended)','Consider a birth class'],\n        'Third trimester':['Pack hospital bag','Discuss a birth plan with your provider','Install infant car seat']\n      };\n      $('pg-checklist').innerHTML = Object.entries(CHECKLIST).map(([section,items])=>`\n        <h5 style=\"font-size:.85rem;margin:14px 0 4px;\">${section}<\/h5>\n        ${items.map((item,i)=>`<label class=\"pg-checklist-item\"><input type=\"checkbox\" data-k=\"${section}-${i}\"> ${item}<\/label>`).join('')}\n      `).join('');\n\n      \/\/ hospital bag checklist\n      const HOSPITAL_BAG={\n        'Mother':['Comfortable going-home outfit','Toiletries','Phone charger','Insurance and ID documents','Nursing bra\/pads if planning to breastfeed'],\n        'Baby':['Going-home outfit','Swaddle blanket','Diapers and wipes','Installed infant car seat'],\n        'Partner':['Change of clothes','Snacks and water','Phone charger','Entertainment for waiting periods']\n      };\n      $('pg-hospital-checklist').innerHTML = Object.entries(HOSPITAL_BAG).map(([section,items])=>`\n        <h5 style=\"font-size:.85rem;margin:14px 0 4px;\">${section}<\/h5>\n        ${items.map((item,i)=>`<label class=\"pg-checklist-item\"><input type=\"checkbox\" data-hb=\"${section}-${i}\"> ${item}<\/label>`).join('')}\n      `).join('');\n      $('pg-hospital-print').onclick=()=>window.print();\n\n      \/\/ baby name planner (session only)\n      let savedNames=[];\n      function renderNames(){\n        $('pg-name-list').innerHTML = savedNames.length\n          ? savedNames.map((n,i)=>`<span class=\"tag-btn active\" style=\"margin:0 6px 6px 0;display:inline-flex;align-items:center;gap:6px;\">${n} <span data-rm=\"${i}\" style=\"cursor:pointer;\">\u2715<\/span><\/span>`).join('')\n          : '<p style=\"color:var(--text-soft);font-size:.85rem;\">No names saved yet.<\/p>';\n        $('pg-name-list').querySelectorAll('[data-rm]').forEach(x=>x.onclick=()=>{ savedNames.splice(+x.dataset.rm,1); renderNames(); });\n      }\n      renderNames();\n      $('pg-name-add').onclick=()=>{\n        const v=$('pg-name-input').value.trim();\n        if(v){ savedNames.push(v); $('pg-name-input').value=''; renderNames(); }\n      };\n\n      const shareText=`My estimated due date is ${fmtLong(dueDate)} (currently ${gaWeeksNow}w ${gaDaysRem}d, ${trimester} trimester). Calculated on MAFtrix.`;\n      $('pg-copy').onclick=()=>{ const done=()=>{$('pg-copy').textContent='Copied!';setTimeout(()=>$('pg-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('pg-print').onclick=()=>window.print();\n      $('pg-ics').onclick=()=>{\n        const icsDate=d=>d.toISOString().slice(0,10).replace(\/-\/g,'');\n        let ics='BEGIN:VCALENDAR\\nVERSION:2.0\\nPRODID:-\/\/MAFtrix\/\/Pregnancy\/\/EN\\n';\n        const addEvent=(date,title)=>{ ics+=`BEGIN:VEVENT\\nUID:${Date.now()}-${Math.random().toString(36).slice(2)}@maftrix\\nDTSTART;VALUE=DATE:${icsDate(date)}\\nSUMMARY:${title}\\nEND:VEVENT\\n`; };\n        addEvent(dueDate,'Estimated Due Date');\n        addEvent(t2start,'Second Trimester Begins');\n        addEvent(t3start,'Third Trimester Begins');\n        ics+='END:VCALENDAR';\n        const blob=new Blob([ics],{type:'text\/calendar'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='pregnancy-dates.ics'; a.click(); URL.revokeObjectURL(url);\n      };\n\n      \/\/ kick counter\n      kickCount=0; kickStart=null; if(kickTimer) clearInterval(kickTimer);\n      function updateKickDisplay(){\n        const elapsed = kickStart ? Math.floor((Date.now()-kickStart)\/1000) : 0;\n        const mm=String(Math.floor(elapsed\/60)).padStart(2,'0'), ss=String(elapsed%60).padStart(2,'0');\n        const disp=$('pg-kick-display'); if(disp) disp.textContent=`${kickCount} kicks \u00b7 ${mm}:${ss}`; else clearInterval(kickTimer);\n      }\n      $('pg-kick-tap').onclick=()=>{\n        if(!kickStart){ kickStart=Date.now(); kickTimer=setInterval(updateKickDisplay,1000); }\n        kickCount++; updateKickDisplay();\n      };\n      $('pg-kick-reset').onclick=()=>{ kickCount=0; kickStart=null; if(kickTimer) clearInterval(kickTimer); updateKickDisplay(); };\n\n      \/\/ contraction timer\n      contractions=[];\n      let activeContractionStart=null;\n      function renderContractions(){\n        if(!contractions.length){ $('pg-contraction-log').innerHTML='<p style=\"color:var(--text-soft);font-size:.85rem;\">No contractions logged yet.<\/p>'; return; }\n        const rows=contractions.slice().reverse().map((c,i)=>{\n          const idx=contractions.length-i;\n          return `<tr><td>#${idx}<\/td><td>${c.duration}s<\/td><td>${c.interval!==null?c.interval+'s':'\u2014'}<\/td><\/tr>`;\n        }).join('');\n        $('pg-contraction-log').innerHTML=`<table class=\"ref-table\"><thead><tr><th>#<\/th><th>Duration<\/th><th>Interval from previous<\/th><\/tr><\/thead><tbody>${rows}<\/tbody><\/table>`;\n      }\n      renderContractions();\n      $('pg-contraction-btn').onclick=(e)=>{\n        if(!activeContractionStart){\n          activeContractionStart=Date.now();\n          e.target.textContent='Stop contraction';\n          e.target.classList.remove('btn-primary'); e.target.classList.add('btn-amber');\n        } else {\n          const duration=Math.round((Date.now()-activeContractionStart)\/1000);\n          const interval = contractions.length ? Math.round((activeContractionStart-contractions[contractions.length-1].endTime)\/1000) : null;\n          contractions.push({duration,interval,endTime:Date.now()});\n          activeContractionStart=null;\n          e.target.textContent='Start contraction';\n          e.target.classList.add('btn-primary'); e.target.classList.remove('btn-amber');\n          renderContractions();\n        }\n      };\n      $('pg-contraction-clear').onclick=()=>{ contractions=[]; renderContractions(); };\n    };\n  }});\n\n\/* ---------- EVERYDAY ---------- *\/\nreg({slug:'age',name:'Age Calculator',cat:'everyday',icn:'calendar',photo:36,\n  desc:'Find your exact age down to the second \u2014 plus zodiac sign, generation, life milestones, planetary ages, and how many World Cups, Olympics and leap years you have lived through.',\n  render(el){\n    let liveTimer=null;\n    function clearLiveTimer(){ if(liveTimer){ clearInterval(liveTimer); liveTimer=null; } }\n\n    function westernZodiac(m,d){\n      const md=m*100+d;\n      if(md>=1222||md<=119) return 'Capricorn \u2651';\n      if(md<=218) return 'Aquarius \u2652';\n      if(md<=320) return 'Pisces \u2653';\n      if(md<=419) return 'Aries \u2648';\n      if(md<=520) return 'Taurus \u2649';\n      if(md<=620) return 'Gemini \u264a';\n      if(md<=722) return 'Cancer \u264b';\n      if(md<=822) return 'Leo \u264c';\n      if(md<=922) return 'Virgo \u264d';\n      if(md<=1022) return 'Libra \u264e';\n      if(md<=1121) return 'Scorpio \u264f';\n      return 'Sagittarius \u2650';\n    }\n    function chineseZodiac(year){\n      const animals=['Rat','Ox','Tiger','Rabbit','Dragon','Snake','Horse','Goat','Monkey','Rooster','Dog','Pig'];\n      return animals[((year-2020)%12+12)%12];\n    }\n    function generationOf(year){\n      if(year>=2013) return 'Generation Alpha';\n      if(year>=1997) return 'Generation Z';\n      if(year>=1981) return 'Millennial';\n      if(year>=1965) return 'Generation X';\n      if(year>=1946) return 'Baby Boomer';\n      if(year>=1928) return 'Silent Generation';\n      return 'Greatest Generation';\n    }\n    function leapYearsExperienced(dob,asOf){\n      let count=0;\n      for(let y=dob.getFullYear();y<=asOf.getFullYear();y++){\n        if((y%4===0&#038;&#038;y%100!==0)||y%400===0){\n          const feb29=new Date(y,1,29);\n          if(feb29>=dob&&feb29<=asOf) count++;\n        }\n      }\n      return count;\n    }\n    function worldCupYears(uptoYear){\n      const years=[]; for(let y=1930;y<=uptoYear;y+=4){ if(y===1942||y===1946) continue; years.push(y); } return years;\n    }\n    function summerOlympicYears(uptoYear){\n      const years=[]; for(let y=1896;y<=uptoYear;y+=4){ if(y===1916||y===1940||y===1944) continue; years.push(y===2020?2021:y); } return years;\n    }\n    function fullMoonsSince(dob,asOf){\n      const ref=new Date(Date.UTC(2000,0,6,18,14,0)), synodic=29.530588853;\n      const c1=((dob-ref)\/86400000)\/synodic, c2=((asOf-ref)\/86400000)\/synodic;\n      return Math.max(0,Math.floor(c2)-Math.floor(c1));\n    }\n    const PLANETS=[['Mercury',87.9691],['Venus',224.701],['Mars',686.980],['Jupiter',4332.59],['Saturn',10759.22],['Uranus',30688.5],['Neptune',60182]];\n    const MILESTONES=[[16,\"Learner's permit eligibility (most US states)\"],[18,\"Legal adult \u2014 can vote in most countries\"],[21,\"Legal drinking age in the US\"],[25,\"Full car-rental eligibility (most US companies)\"],[30,\"Eligible to run for the US Senate\"],[35,\"Eligible to run for US President\"],[50,\"AARP membership eligibility (US)\"],[62,\"Early Social Security eligibility (US)\"],[65,\"Medicare eligibility (US)\"],[67,\"Full Social Security retirement age (US)\"],[100,\"Centenarian milestone\"]];\n\n    function ageParts(dob,asOf){\n      let years=asOf.getFullYear()-dob.getFullYear(), months=asOf.getMonth()-dob.getMonth(), days=asOf.getDate()-dob.getDate();\n      if(days<0){ months--; days+=new Date(asOf.getFullYear(),asOf.getMonth(),0).getDate(); }\n      if(months<0){ years--; months+=12; }\n      return {years,months,days};\n    }\n\n    el.innerHTML = `\n      <div class=\"age-shell\">\n      <div class=\"seg\" id=\"ag-mode-seg\">\n        <button class=\"active\" data-v=\"live\">Your age<\/button>\n        <button data-v=\"date\">Age on a date<\/button>\n        <button data-v=\"compare\">Compare two people<\/button>\n      <\/div>\n      <div class=\"field\"><label id=\"ag-dob-label\">Date of birth<\/label><input type=\"date\" id=\"ag-dob\"><\/div>\n      <div class=\"field\" id=\"ag-target-field\" hidden><label>Calculate age as of<\/label><input type=\"date\" id=\"ag-target\"><\/div>\n      <div class=\"field\" id=\"ag-dob2-field\" hidden><label>Second person's date of birth<\/label><input type=\"date\" id=\"ag-dob2\"><\/div>\n      <button class=\"btn btn-primary btn-block\" id=\"ag-btn\">Calculate age<\/button>\n      <div class=\"readout\" id=\"ag-out\" hidden><\/div>\n      <div id=\"ag-results\"><\/div>\n      <\/div>`;\n\n    let mode='live';\n    $('ag-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('ag-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('ag-target-field').hidden = mode!=='date';\n      $('ag-dob2-field').hidden = mode!=='compare';\n      $('ag-dob-label').textContent = mode==='compare' ? \"First person's date of birth\" : 'Date of birth';\n      clearLiveTimer();\n      $('ag-out').hidden=true; $('ag-results').innerHTML='';\n    });\n\n    function renderCompare(dob1,dob2){\n      clearLiveTimer();\n      const today=new Date();\n      const older = dob1<dob2 ? dob1 : dob2, younger = dob1<dob2 ? dob2 : dob1;\n      const diff = ageParts(older,younger);\n      const a1=ageParts(dob1,today), a2=ageParts(dob2,today);\n      readout('ag-out', `${diff.years}y ${diff.months}m ${diff.days}d apart`, dob1<dob2?'Person 1 is older':'Person 2 is older');\n      $('ag-results').innerHTML = `<div class=\"calc-section section-blue\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('calendar',16)}<\/div><h5>Both ages today<\/h5><\/div>\n        <div class=\"summary-mini\">\n          <div class=\"m-item\"><span>Person 1<\/span><b>${a1.years}y ${a1.months}m ${a1.days}d<\/b><\/div>\n          <div class=\"m-item\"><span>Person 2<\/span><b>${a2.years}y ${a2.months}m ${a2.days}d<\/b><\/div>\n        <\/div>\n      <\/div>`;\n    }\n\n    function renderFull(dob, asOf, isLive){\n      const parts = ageParts(dob,asOf);\n      readout('ag-out', `${parts.years} years, ${parts.months} months, ${parts.days} days`, isLive?'Your current age':'Age as of the selected date');\n\n      const totalMs = asOf-dob;\n      const totalDays = Math.floor(totalMs\/86400000);\n      const totalMinutesAlive = Math.floor(totalMs\/60000);\n\n      let next=new Date(asOf.getFullYear(),dob.getMonth(),dob.getDate());\n      if(next<asOf) next.setFullYear(next.getFullYear()+1);\n      const toBday = Math.ceil((next-asOf)\/86400000);\n\n      const lifeExpectancyYears=73;\n      const lifePct = Math.min(100, (parts.years + parts.months\/12) \/ lifeExpectancyYears * 100);\n\n      const heartbeats = totalMinutesAlive*72;\n      const breaths = totalMinutesAlive*16;\n      const sleepYears = (totalDays\/3)\/365.25;\n\n      const leaps = leapYearsExperienced(dob,asOf);\n      const cups = worldCupYears(asOf.getFullYear()).filter(y=>y>=dob.getFullYear()&&y<=asOf.getFullYear()).length;\n      const olys = summerOlympicYears(asOf.getFullYear()).filter(y=>y>=dob.getFullYear()&&y<=asOf.getFullYear()).length;\n      const moons = fullMoonsSince(dob,asOf);\n\n      const day10k = new Date(dob.getTime()+10000*86400000);\n      const day20k = new Date(dob.getTime()+20000*86400000);\n      const billionSec = new Date(dob.getTime()+1000000000*1000);\n      const half = new Date(dob); half.setMonth(half.getMonth()+6);\n      const fmtDate = d=>d.toLocaleDateString('en-US',{year:'numeric',month:'long',day:'numeric'});\n      const milestoneRow = (label,date)=>{\n        const passed = date<=asOf;\n        return `<tr><td>${label}<\/td><td>${fmtDate(date)}<\/td><td>${passed?'\u2705 Reached':'\u23f3 '+Math.ceil((date-asOf)\/86400000).toLocaleString()+' days to go'}<\/td><\/tr>`;\n      };\n\n      $('ag-results').innerHTML = `\n        <div class=\"calc-section section-blue\" id=\"ag-live-section\" ${isLive?'':'hidden'}>\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Live counter<\/h5><\/div>\n          <div class=\"summary-mini\" id=\"ag-live\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Fun facts<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Day of the week born<\/span><b>${dob.toLocaleDateString('en-US',{weekday:'long'})}<\/b><\/div>\n            <div class=\"m-item\"><span>Western zodiac<\/span><b>${westernZodiac(dob.getMonth()+1,dob.getDate())}<\/b><\/div>\n            <div class=\"m-item\"><span>Chinese zodiac<\/span><b>${chineseZodiac(dob.getFullYear())}<\/b><\/div>\n            <div class=\"m-item\"><span>Generation<\/span><b>${generationOf(dob.getFullYear())}<\/b><\/div>\n            <div class=\"m-item\"><span>Est. heartbeats so far<\/span><b>${heartbeats.toLocaleString()}<\/b><\/div>\n            <div class=\"m-item\"><span>Est. breaths taken<\/span><b>${breaths.toLocaleString()}<\/b><\/div>\n            <div class=\"m-item\"><span>Est. time spent asleep<\/span><b>${sleepYears.toFixed(1)} yrs<\/b><\/div>\n            <div class=\"m-item\"><span>Days to next birthday<\/span><b>${toBday}<\/b><\/div>\n          <\/div>\n          <div style=\"margin-top:16px;\">\n            <div style=\"display:flex;justify-content:space-between;font-size:.8rem;color:var(--text-soft);margin-bottom:6px;\"><span>Life progress (vs. 73-yr global average)<\/span><span>${lifePct.toFixed(0)}%<\/span><\/div>\n            <div style=\"background:var(--line);border-radius:20px;height:10px;overflow:hidden;\"><div style=\"background:linear-gradient(90deg,var(--teal),var(--amber));height:100%;width:${lifePct.toFixed(1)}%;\"><\/div><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>What you've lived through<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Leap years experienced<\/span><b>${leaps}<\/b><\/div>\n            <div class=\"m-item\"><span>FIFA World Cups<\/span><b>${cups}<\/b><\/div>\n            <div class=\"m-item\"><span>Summer Olympic Games<\/span><b>${olys}<\/b><\/div>\n            <div class=\"m-item\"><span>Full moons (approx.)<\/span><b>${moons.toLocaleString()}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('home',16)}<\/div><h5>Your age on other planets<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Planet<\/th><th>Age<\/th><\/tr><\/thead><tbody>\n            ${PLANETS.map(([name,period])=>`<tr><td>${name}<\/td><td>${(totalDays\/period).toFixed(2)} ${name} years<\/td><\/tr>`).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ticket',16)}<\/div><h5>Special day milestones<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Milestone<\/th><th>Date<\/th><th>Status<\/th><\/tr><\/thead><tbody>\n            ${milestoneRow('10,000 days old', day10k)}\n            ${milestoneRow('20,000 days old', day20k)}\n            ${milestoneRow('1 billion seconds old', billionSec)}\n            <tr><td>Half birthday<\/td><td>${fmtDate(half)}<\/td><td>\u2014<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('grad',16)}<\/div><h5>Age milestone tracker<\/h5><\/div>\n          <table class=\"ref-table\"><thead><tr><th>Age<\/th><th>Milestone<\/th><th>Status<\/th><\/tr><\/thead><tbody>\n            ${MILESTONES.map(([age,label])=>{\n              const reachDate=new Date(dob.getFullYear()+age,dob.getMonth(),dob.getDate());\n              const reached = reachDate<=asOf;\n              return `<tr><td>${age}<\/td><td>${label}<\/td><td>${reached?'\u2705 Reached':'\u23f3 '+fmtDate(reachDate)}<\/td><\/tr>`;\n            }).join('')}\n          <\/tbody><\/table>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Share your result<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"ag-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"ag-print\" type=\"button\">Print \/ save PDF<\/button>\n          <\/div>\n          <div class=\"form-row\" style=\"margin-top:10px;\">\n            <a class=\"btn btn-ghost btn-block\" id=\"ag-share-wa\" target=\"_blank\" rel=\"noopener\">Share on WhatsApp<\/a>\n            <a class=\"btn btn-ghost btn-block\" id=\"ag-share-x\" target=\"_blank\" rel=\"noopener\">Share on X<\/a>\n          <\/div>\n        <\/div>`;\n\n      const shareText = `I am ${parts.years} years, ${parts.months} months and ${parts.days} days old! Calculated on MAFtrix.`;\n      $('ag-share-wa').href = 'https:\/\/wa.me\/?text='+encodeURIComponent(shareText);\n      $('ag-share-x').href = 'https:\/\/twitter.com\/intent\/tweet?text='+encodeURIComponent(shareText);\n      $('ag-copy').onclick = ()=>{\n        const done=()=>{ $('ag-copy').textContent='Copied!'; setTimeout(()=>$('ag-copy').textContent='Copy result',1500); };\n        if(navigator.clipboard) navigator.clipboard.writeText(shareText).then(done).catch(done);\n        else done();\n      };\n      $('ag-print').onclick = ()=> window.print();\n\n      if(isLive){\n        clearLiveTimer();\n        function tick(){\n          const liveEl=$('ag-live');\n          if(!liveEl){ clearLiveTimer(); return; }\n          const now=new Date();\n          const ms = now-dob;\n          const totalSec=Math.floor(ms\/1000);\n          liveEl.innerHTML = [\n            ['Weeks',Math.floor(ms\/(7*86400000)).toLocaleString()],\n            ['Days',Math.floor(ms\/86400000).toLocaleString()],\n            ['Hours',Math.floor(ms\/3600000).toLocaleString()],\n            ['Minutes',Math.floor(ms\/60000).toLocaleString()],\n            ['Seconds',totalSec.toLocaleString()]\n          ].map(([l,v])=>`<div class=\"m-item\"><span>${l}<\/span><b>${v}<\/b><\/div>`).join('');\n        }\n        tick();\n        liveTimer=setInterval(tick,1000);\n      }\n    }\n\n    $('ag-btn').onclick=()=>{\n      clearLiveTimer();\n      if(mode==='compare'){\n        const v1=$('ag-dob').value, v2=$('ag-dob2').value;\n        if(!v1||!v2){ readoutErr('ag-out','Please select both dates of birth.'); return; }\n        renderCompare(new Date(v1), new Date(v2));\n        return;\n      }\n      const v=$('ag-dob').value;\n      if(!v){ readoutErr('ag-out','Please select your date of birth.'); return; }\n      const dob=new Date(v);\n      const today=new Date();\n      if(dob>today){ readoutErr('ag-out','Date of birth must be in the past.'); return; }\n      if(mode==='date'){\n        const tv=$('ag-target').value;\n        if(!tv){ readoutErr('ag-out','Please select the date to calculate age as of.'); return; }\n        renderFull(dob, new Date(tv), false);\n      } else {\n        renderFull(dob, today, true);\n      }\n    };\n  }});\n\nreg({slug:'date-difference',name:'Date Difference Calculator',cat:'everyday',icn:'calendar',photo:37,\n  desc:'A complete date and time hub: exact difference, business days with holidays, date add\/subtract, recurring dates, and live countdowns.',\n  render(el){\n    let liveTimer=null;\n    function clearLiveTimer(){ if(liveTimer){ clearInterval(liveTimer); liveTimer=null; } }\n\n    function easterDate(year){\n      const a=year%19,b=Math.floor(year\/100),c=year%100,d=Math.floor(b\/4),e=b%4,f=Math.floor((b+8)\/25),g=Math.floor((b-f+1)\/3),h=(19*a+b-d-g+15)%30,i=Math.floor(c\/4),k=c%4,l=(32+2*e+2*i-h-k)%7,m=Math.floor((a+11*h+22*l)\/451),month=Math.floor((h+l-7*m+114)\/31),day=((h+l-7*m+114)%31)+1;\n      return new Date(year,month-1,day);\n    }\n    function nthWeekday(year,month,weekday,n){\n      let d=new Date(year,month,1), count=0;\n      while(true){ if(d.getDay()===weekday){ count++; if(count===n) return new Date(d); } d.setDate(d.getDate()+1); }\n    }\n    function lastWeekday(year,month,weekday){\n      let d=new Date(year,month+1,0);\n      while(d.getDay()!==weekday) d.setDate(d.getDate()-1);\n      return d;\n    }\n    function holidaysForYear(country,year){\n      const list=[];\n      const add=(m,d,name)=>list.push({date:new Date(year,m,d),name});\n      const easter=easterDate(year);\n      const goodFriday=new Date(easter); goodFriday.setDate(easter.getDate()-2);\n      const easterMon=new Date(easter); easterMon.setDate(easter.getDate()+1);\n      if(country==='US'){\n        add(0,1,\"New Year's Day\"); list.push({date:nthWeekday(year,0,1,3),name:'MLK Day'}); list.push({date:nthWeekday(year,1,1,3),name:\"Presidents Day\"});\n        list.push({date:lastWeekday(year,4,1),name:'Memorial Day'}); add(5,19,'Juneteenth'); add(6,4,'Independence Day');\n        list.push({date:nthWeekday(year,8,1,1),name:'Labor Day'}); list.push({date:nthWeekday(year,10,4,4),name:'Thanksgiving'}); add(11,25,'Christmas');\n      } else if(country==='UK'){\n        add(0,1,\"New Year's Day\"); list.push({date:goodFriday,name:'Good Friday'}); list.push({date:easterMon,name:'Easter Monday'});\n        list.push({date:nthWeekday(year,4,1,1),name:'Early May Bank Holiday'}); list.push({date:lastWeekday(year,4,1),name:'Spring Bank Holiday'});\n        list.push({date:lastWeekday(year,7,1),name:'Summer Bank Holiday'}); add(11,25,'Christmas'); add(11,26,'Boxing Day');\n      } else if(country==='CA'){\n        add(0,1,\"New Year's Day\"); list.push({date:goodFriday,name:'Good Friday'}); add(6,1,'Canada Day');\n        list.push({date:nthWeekday(year,8,1,1),name:'Labour Day'}); list.push({date:nthWeekday(year,9,1,2),name:'Thanksgiving'}); add(11,25,'Christmas'); add(11,26,'Boxing Day');\n      } else if(country==='AU'){\n        add(0,1,\"New Year's Day\"); add(0,26,'Australia Day'); list.push({date:goodFriday,name:'Good Friday'}); list.push({date:easterMon,name:'Easter Monday'});\n        list.push({date:nthWeekday(year,5,1,2),name:\"King's Birthday\"}); add(11,25,'Christmas'); add(11,26,'Boxing Day');\n      } else if(country==='PK'){\n        add(1,5,'Kashmir Day'); add(2,23,'Pakistan Day'); add(4,1,'Labour Day'); add(7,14,'Independence Day'); add(10,9,'Iqbal Day'); add(11,25,'Quaid-e-Azam Day');\n      } else if(country==='IN'){\n        add(0,26,'Republic Day'); add(7,15,'Independence Day'); add(9,2,'Gandhi Jayanti');\n      }\n      return list;\n    }\n    function fmtLong(d){ return d.toLocaleDateString('en-US',{weekday:'long',year:'numeric',month:'long',day:'numeric'}); }\n\n    el.innerHTML = `\n      <div class=\"dd-shell\">\n      <div class=\"seg\" id=\"dd-mode-seg\">\n        <button class=\"active\" data-v=\"diff\">Difference<\/button>\n        <button data-v=\"addsub\">Add \/ Subtract<\/button>\n        <button data-v=\"recur\">Recurring dates<\/button>\n        <button data-v=\"countdown\">Countdown<\/button>\n      <\/div>\n\n      <div id=\"dd-diff-fields\">\n        <div class=\"field\"><label>Purpose<\/label>\n          <select id=\"dd-purpose\">\n            <option value=\"general\">General date difference<\/option>\n            <option value=\"work\">Work experience<\/option>\n            <option value=\"relationship\">Relationship duration<\/option>\n            <option value=\"project\">Project duration<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Start date<\/label><input type=\"date\" id=\"dd-a\"><\/div>\n          <div class=\"field\"><label>End date<\/label><input type=\"date\" id=\"dd-b\"><\/div>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Start time <small>(optional)<\/small><\/label><input type=\"time\" id=\"dd-a-time\"><\/div>\n          <div class=\"field\"><label>End time <small>(optional)<\/small><\/label><input type=\"time\" id=\"dd-b-time\"><\/div>\n        <\/div>\n        <div class=\"inline-fields\" style=\"margin-bottom:10px;\">\n          <button class=\"tag-btn active\" data-mode=\"inclusive\">Inclusive counting<\/button>\n          <button class=\"tag-btn\" data-mode=\"exclusive\">Exclusive counting<\/button>\n        <\/div>\n        <span class=\"more-link\" id=\"dd-more-link\">+ Business day &amp; holiday options<\/span>\n        <div class=\"adv-panel more-panel-card\" id=\"dd-more-panel\">\n          <div class=\"section-tag\">Weekend definition<\/div>\n          <div class=\"inline-fields\" style=\"margin-bottom:14px;\">\n            <button class=\"tag-btn active\" data-wd=\"6\">Sat<\/button>\n            <button class=\"tag-btn active\" data-wd=\"0\">Sun<\/button>\n            <button class=\"tag-btn\" data-wd=\"5\">Fri<\/button>\n            <button class=\"tag-btn\" data-wd=\"1\">Mon<\/button>\n          <\/div>\n          <div class=\"section-tag\">Public holidays<\/div>\n          <div class=\"field\"><label>Country (illustrative major holidays only)<\/label>\n            <select id=\"dd-country\">\n              <option value=\"none\">None<\/option>\n              <option value=\"US\">United States<\/option>\n              <option value=\"UK\">United Kingdom<\/option>\n              <option value=\"CA\">Canada<\/option>\n              <option value=\"AU\">Australia<\/option>\n              <option value=\"PK\">Pakistan<\/option>\n              <option value=\"IN\">India<\/option>\n            <\/select>\n          <\/div>\n        <\/div>\n        <button class=\"btn btn-primary btn-block\" id=\"dd-btn\">Calculate difference<\/button>\n      <\/div>\n\n      <div id=\"dd-addsub-fields\" hidden>\n        <div class=\"field\"><label>Base date<\/label><input type=\"date\" id=\"as-base\"><\/div>\n        <div class=\"inline-fields\" style=\"margin-bottom:10px;\">\n          <button class=\"tag-btn active\" data-op=\"add\">Add<\/button>\n          <button class=\"tag-btn\" data-op=\"sub\">Subtract<\/button>\n        <\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Amount<\/label><input type=\"number\" id=\"as-amount\" value=\"90\" min=\"1\"><\/div>\n          <div class=\"field\"><label>Unit<\/label><select id=\"as-unit\"><option value=\"days\">Days<\/option><option value=\"weeks\">Weeks<\/option><option value=\"months\">Months<\/option><option value=\"years\">Years<\/option><\/select><\/div>\n        <\/div>\n        <button class=\"btn btn-primary btn-block\" id=\"as-btn\">Calculate date<\/button>\n        <div class=\"readout\" id=\"as-out\" hidden><\/div>\n      <\/div>\n\n      <div id=\"dd-recur-fields\" hidden>\n        <div class=\"field\"><label>Start date<\/label><input type=\"date\" id=\"rc-start\"><\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Repeat every<\/label><input type=\"number\" id=\"rc-every\" value=\"2\" min=\"1\"><\/div>\n          <div class=\"field\"><label>Unit<\/label><select id=\"rc-unit\"><option value=\"days\">Days<\/option><option value=\"weeks\">Weeks<\/option><option value=\"months\">Months<\/option><option value=\"years\">Years<\/option><\/select><\/div>\n        <\/div>\n        <div class=\"field\"><label>Number of occurrences<\/label><input type=\"number\" id=\"rc-count\" value=\"10\" min=\"1\" max=\"100\"><\/div>\n        <button class=\"btn btn-primary btn-block\" id=\"rc-btn\">Generate dates<\/button>\n        <div id=\"rc-out\"><\/div>\n      <\/div>\n\n      <div id=\"dd-countdown-fields\" hidden>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Target date<\/label><input type=\"date\" id=\"cd-date\"><\/div>\n          <div class=\"field\"><label>Target time <small>(optional)<\/small><\/label><input type=\"time\" id=\"cd-time\"><\/div>\n        <\/div>\n        <button class=\"btn btn-primary btn-block\" id=\"cd-btn\">Start countdown<\/button>\n        <div class=\"readout\" id=\"cd-out\" hidden><\/div>\n        <div class=\"summary-mini\" id=\"cd-live\"><\/div>\n      <\/div>\n\n      <div id=\"dd-results\"><\/div>\n      <\/div>`;\n\n    \/\/ ---------- mode switching ----------\n    let mode='diff';\n    $('dd-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('dd-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v; clearLiveTimer();\n      $('dd-diff-fields').hidden = mode!=='diff';\n      $('dd-addsub-fields').hidden = mode!=='addsub';\n      $('dd-recur-fields').hidden = mode!=='recur';\n      $('dd-countdown-fields').hidden = mode!=='countdown';\n      $('dd-results').innerHTML='';\n    });\n    $('dd-more-link').onclick=()=>$('dd-more-panel').classList.toggle('show');\n    let countMode='inclusive';\n    el.querySelector('#dd-diff-fields .inline-fields').querySelectorAll('[data-mode]').forEach(b=>b.onclick=()=>{\n      el.querySelectorAll('[data-mode]').forEach(x=>x.classList.remove('active')); b.classList.add('active'); countMode=b.dataset.mode;\n    });\n    const weekendDays=new Set([6,0]);\n    el.querySelectorAll('[data-wd]').forEach(b=>b.onclick=()=>{\n      b.classList.toggle('active'); const wd=+b.dataset.wd;\n      if(b.classList.contains('active')) weekendDays.add(wd); else weekendDays.delete(wd);\n    });\n\n    \/\/ ================= DIFFERENCE MODE =================\n    $('dd-btn').onclick=()=>{\n      const av=$('dd-a').value, bv=$('dd-b').value;\n      if(!av||!bv){ $('dd-results').innerHTML=''; readoutErrInto('dd-results','Please select both dates.'); return; }\n      const atime=$('dd-a-time').value||'00:00', btime=$('dd-b-time').value||'00:00';\n      let d1=new Date(av+'T'+atime), d2=new Date(bv+'T'+btime);\n      if(d1>d2){ [d1,d2]=[d2,d1]; }\n      let totalMs = d2-d1;\n      let totalDays = Math.floor(totalMs\/86400000);\n      if(countMode==='inclusive') totalDays += 1;\n\n      let years=d2.getFullYear()-d1.getFullYear(), months=d2.getMonth()-d1.getMonth(), days=d2.getDate()-d1.getDate();\n      if(days<0){ months--; days+=new Date(d2.getFullYear(),d2.getMonth(),0).getDate(); }\n      if(months<0){ years--; months+=12; }\n\n      let businessDays=0, weekendCount=0, holidayHits=[];\n      const country=$('dd-country').value;\n      const holidaySet=new Set();\n      if(country!=='none'){\n        for(let y=d1.getFullYear(); y<=d2.getFullYear(); y++){\n          holidaysForYear(country,y).forEach(h=>holidaySet.add(h.date.toDateString()+'|'+h.name));\n        }\n      }\n      const iterEnd=new Date(d1); iterEnd.setHours(0,0,0,0);\n      const iterStop=new Date(d2); iterStop.setHours(0,0,0,0);\n      for(let d=new Date(iterEnd); d<=iterStop; d.setDate(d.getDate()+1)){\n        const isWeekend=weekendDays.has(d.getDay());\n        if(isWeekend) weekendCount++;\n        let isHoliday=false;\n        if(country!=='none'){\n          for(const entry of holidaySet){ if(entry.startsWith(d.toDateString()+'|')){ isHoliday=true; holidayHits.push(entry.split('|')[1]+' ('+d.toLocaleDateString()+')'); break; } }\n        }\n        if(!isWeekend &#038;&#038; !isHoliday) businessDays++;\n      }\n\n      let leapCrossed=0;\n      for(let y=d1.getFullYear(); y<=d2.getFullYear(); y++){ if((y%4===0&#038;&#038;y%100!==0)||y%400===0) leapCrossed++; }\n\n      const purposeLabels={general:'Date difference',work:'Work experience',relationship:'Relationship duration',project:'Project duration'};\n      const purpose=$('dd-purpose').value;\n\n      $('dd-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${years}y ${months}m ${days}d<\/div><div class=\"dd-main-sub\">${fmtLong(d1)} \u2192 ${fmtLong(d2)} \u00b7 ${countMode} counting<\/div><\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Summary cards<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>\ud83d\udcc5 Total days<\/span><b>${totalDays.toLocaleString()}<\/b><\/div>\n            <div class=\"m-item\"><span>\ud83d\uddd3 Total weeks<\/span><b>${(totalDays\/7).toFixed(1)}<\/b><\/div>\n            <div class=\"m-item\"><span>\ud83d\udcbc Business days<\/span><b>${businessDays.toLocaleString()}<\/b><\/div>\n            <div class=\"m-item\"><span>\ud83c\udf34 Weekend days<\/span><b>${weekendCount.toLocaleString()}<\/b><\/div>\n            <div class=\"m-item\"><span>\u23f0 Total hours<\/span><b>${Math.floor(totalMs\/3600000).toLocaleString()}<\/b><\/div>\n            <div class=\"m-item\"><span>\u23f1 Total minutes<\/span><b>${Math.floor(totalMs\/60000).toLocaleString()}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('calendar',16)}<\/div><h5>Full breakdown<\/h5><\/div>\n          <table class=\"ref-table\">\n            <tbody>\n              <tr><td>Years \/ Months \/ Days<\/td><td>${years}y ${months}m ${days}d<\/td><\/tr>\n              <tr><td>Total calendar days<\/td><td>${totalDays.toLocaleString()}<\/td><\/tr>\n              <tr><td>Total weeks<\/td><td>${(totalDays\/7).toFixed(2)}<\/td><\/tr>\n              <tr><td>Total hours<\/td><td>${Math.floor(totalMs\/3600000).toLocaleString()}<\/td><\/tr>\n              <tr><td>Total minutes<\/td><td>${Math.floor(totalMs\/60000).toLocaleString()}<\/td><\/tr>\n              <tr><td>Total seconds<\/td><td>${Math.floor(totalMs\/1000).toLocaleString()}<\/td><\/tr>\n              <tr><td>Leap years crossed<\/td><td>${leapCrossed}<\/td><\/tr>\n              ${country!=='none'?`<tr><td>Public holidays excluded<\/td><td>${holidayHits.length}<\/td><\/tr>`:''}\n            <\/tbody>\n          <\/table>\n          ${country!=='none' && holidayHits.length ? `<p style=\"font-size:.78rem;color:var(--text-soft);margin-top:10px;\">Holidays counted: ${holidayHits.slice(0,8).join(', ')}${holidayHits.length>8?'\u2026':''}<\/p>`:''}\n          <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:10px;\">\u26a0 Public holiday sets are illustrative (major fixed and common floating holidays only) \u2014 not an authoritative payroll or legal calendar.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>Timeline<\/h5><\/div>\n          <div class=\"dd-timeline\" id=\"dd-timeline-el\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('calendar',16)}<\/div><h5>Calendar view<\/h5><\/div>\n          <div id=\"dd-cal-el\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"dd-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"dd-print\" type=\"button\">Print<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"dd-csv\" type=\"button\">Export CSV<\/button>\n          <\/div>\n        <\/div>`;\n\n      renderTimeline('dd-timeline-el', d1, d2);\n      renderCalendars('dd-cal-el', d1, d2, weekendDays);\n\n      const shareText = `${purposeLabels[purpose]}: ${years} years, ${months} months, ${days} days (${totalDays.toLocaleString()} total days) between ${d1.toLocaleDateString()} and ${d2.toLocaleDateString()}.`;\n      $('dd-copy').onclick=()=>{ const done=()=>{$('dd-copy').textContent='Copied!';setTimeout(()=>$('dd-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('dd-print').onclick=()=>window.print();\n      $('dd-csv').onclick=()=>{\n        const csv='Metric,Value\\n'+`Years,${years}\\nMonths,${months}\\nDays,${days}\\nTotal Days,${totalDays}\\nBusiness Days,${businessDays}\\nWeekend Days,${weekendCount}\\nTotal Hours,${Math.floor(totalMs\/3600000)}\\n`;\n        const blob=new Blob([csv],{type:'text\/csv'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='date-difference.csv'; a.click(); URL.revokeObjectURL(url);\n      };\n    };\n\n    function renderTimeline(id,d1,d2){\n      const totalSpan=d2-d1;\n      const today=new Date();\n      const pts=[{date:d1,label:'Start'},{date:d2,label:'End'}];\n      if(today>d1 && today<d2) pts.push({date:today,label:'Today'});\n      let html='<div class=\"track\"><\/div><div class=\"fill\" style=\"left:0;width:100%;\"><\/div>';\n      pts.forEach(p=>{\n        const pct = totalSpan>0 ? ((p.date-d1)\/totalSpan*100) : 0;\n        html+=`<div class=\"pt\" style=\"left:${pct}%;\"><\/div><div class=\"pt-label${p.label==='Today'?' bottom':''}\" style=\"left:${pct}%;\">${p.label}: ${p.date.toLocaleDateString()}<\/div>`;\n      });\n      $(id).innerHTML=html;\n    }\n    function renderCalendars(id,d1,d2,weekendSet){\n      function monthGrid(year,month,rangeStart,rangeEnd){\n        const first=new Date(year,month,1), startDow=first.getDay(), daysInMonth=new Date(year,month+1,0).getDate();\n        let html=`<div class=\"dd-cal-label\">${first.toLocaleDateString('en-US',{month:'long',year:'numeric'})}<\/div><div class=\"dd-cal-grid\">`;\n        ['S','M','T','W','T','F','S'].forEach(d=>html+=`<div class=\"dow\">${d}<\/div>`);\n        for(let i=0;i<startDow;i++) html+='<div class=\"day empty\"><\/div>';\n        for(let day=1; day<=daysInMonth; day++){\n          const d=new Date(year,month,day);\n          const inRange = d>=rangeStart && d<=rangeEnd;\n          const isWeekend = weekendSet.has(d.getDay());\n          html+=`<div class=\"day${inRange?' in-range':''}${isWeekend?' weekend':''}\">${day}<\/div>`;\n        }\n        html+='<\/div>';\n        return html;\n      }\n      const r1=new Date(d1); r1.setHours(0,0,0,0);\n      const r2=new Date(d2); r2.setHours(0,0,0,0);\n      let out = monthGrid(d1.getFullYear(),d1.getMonth(),r1,r2);\n      if(d1.getFullYear()!==d2.getFullYear() || d1.getMonth()!==d2.getMonth()){\n        out += monthGrid(d2.getFullYear(),d2.getMonth(),r1,r2);\n      }\n      $(id).innerHTML = out;\n    }\n    function readoutErrInto(containerId,msg){\n      $(containerId).innerHTML = `<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">${msg}<\/div><\/div>`;\n    }\n\n    \/\/ ================= ADD\/SUBTRACT MODE =================\n    let asOp='add';\n    el.querySelector('#dd-addsub-fields .inline-fields').querySelectorAll('[data-op]').forEach(b=>b.onclick=()=>{\n      el.querySelectorAll('[data-op]').forEach(x=>x.classList.remove('active')); b.classList.add('active'); asOp=b.dataset.op;\n    });\n    $('as-btn').onclick=()=>{\n      const base=$('as-base').value;\n      if(!base){ readoutErr('as-out','Please select a base date.'); return; }\n      const d=new Date(base);\n      const amt=(+$('as-amount').value||0) * (asOp==='add'?1:-1);\n      const unit=$('as-unit').value;\n      const result=new Date(d);\n      if(unit==='days') result.setDate(result.getDate()+amt);\n      else if(unit==='weeks') result.setDate(result.getDate()+amt*7);\n      else if(unit==='months') result.setMonth(result.getMonth()+amt);\n      else if(unit==='years') result.setFullYear(result.getFullYear()+amt);\n      readout('as-out', `${asOp==='add'?'Adding':'Subtracting'} ${$('as-amount').value} ${unit}`, fmtLong(result));\n    };\n\n    \/\/ ================= RECURRING MODE =================\n    $('rc-btn').onclick=()=>{\n      const start=$('rc-start').value;\n      if(!start){ $('rc-out').innerHTML=''; readoutErrInto('rc-out','Please select a start date.'); return; }\n      const every=+$('rc-every').value||1, unit=$('rc-unit').value, count=Math.min(100,+$('rc-count').value||1);\n      let d=new Date(start);\n      const rows=[];\n      for(let i=0;i<count;i++){\n        rows.push(`<tr><td>${i+1}<\/td><td>${d.toLocaleDateString('en-US',{weekday:'short',year:'numeric',month:'short',day:'numeric'})}<\/td><\/tr>`);\n        const next=new Date(d);\n        if(unit==='days') next.setDate(next.getDate()+every);\n        else if(unit==='weeks') next.setDate(next.getDate()+every*7);\n        else if(unit==='months') next.setMonth(next.getMonth()+every);\n        else if(unit==='years') next.setFullYear(next.getFullYear()+every);\n        d=next;\n      }\n      $('rc-out').innerHTML = `<div class=\"schedule-wrap\" style=\"margin-top:16px;\"><table class=\"mini\"><thead><tr><th>#<\/th><th>Date<\/th><\/tr><\/thead><tbody>${rows.join('')}<\/tbody><\/table><\/div>`;\n    };\n\n    \/\/ ================= COUNTDOWN MODE =================\n    $('cd-btn').onclick=()=>{\n      const dv=$('cd-date').value;\n      if(!dv){ readoutErr('cd-out','Please select a target date.'); return; }\n      const tv=$('cd-time').value||'00:00';\n      const target=new Date(dv+'T'+tv);\n      clearLiveTimer();\n      function tick(){\n        const liveEl=$('cd-live');\n        if(!liveEl){ clearLiveTimer(); return; }\n        const now=new Date();\n        const ms=target-now;\n        const past = ms<0;\n        const abs=Math.abs(ms);\n        readout('cd-out', past?'This event has passed':'Counting down to', fmtLong(target), past?[['Days since',Math.floor(abs\/86400000).toLocaleString()]]:null);\n        liveEl.innerHTML = [\n          ['Days',Math.floor(abs\/86400000).toLocaleString()],\n          ['Hours',Math.floor(abs\/3600000%24)],\n          ['Minutes',Math.floor(abs\/60000%60)],\n          ['Seconds',Math.floor(abs\/1000%60)]\n        ].map(([l,v])=>`<div class=\"m-item\"><span>${l}<\/span><b>${v}<\/b><\/div>`).join('');\n      }\n      tick();\n      liveTimer=setInterval(tick,1000);\n    };\n  }});\n\nreg({slug:'gpa',name:'GPA Calculator',cat:'everyday',icn:'grad',photo:39,\n  desc:'A complete academic GPA hub: multiple grading systems, cumulative GPA, target GPA planning, credit analysis, trend charts, honors prediction and scholarship eligibility.',\n  render(el){\n    const GRADE_SYSTEMS={\n      us4:{label:'US 4.0 Scale',max:4.0,lowerBetter:false,grades:{'A':4,'A-':3.7,'B+':3.3,'B':3,'B-':2.7,'C+':2.3,'C':2,'C-':1.7,'D+':1.3,'D':1,'F':0}},\n      us43:{label:'US 4.3 Scale',max:4.3,lowerBetter:false,grades:{'A+':4.3,'A':4,'A-':3.7,'B+':3.3,'B':3,'B-':2.7,'C+':2.3,'C':2,'C-':1.7,'D':1,'F':0}},\n      cgpa10:{label:'10-Point CGPA (India\/Pakistan)',max:10,lowerBetter:false,grades:{'O':10,'A+':9,'A':8,'B+':7,'B':6,'C':5,'D':4,'F':0}},\n      au7:{label:'Australia 7-Point',max:7,lowerBetter:false,grades:{'HD':7,'D':6,'C':5,'P':4,'F':0}},\n      gulf5:{label:'Gulf 5.0 Scale (Saudi\/UAE)',max:5,lowerBetter:false,grades:{'A+':5,'A':4.75,'B+':4.5,'B':4,'C+':3.5,'C':3,'D':2,'F':0}},\n      de:{label:'Germany 1.0\u20135.0 (lower is better)',max:5,lowerBetter:true,grades:{'1.0':1,'1.3':1.3,'1.7':1.7,'2.0':2,'2.3':2.3,'2.7':2.7,'3.0':3,'3.3':3.3,'3.7':3.7,'4.0':4,'5.0':5}},\n      percent:{label:'Percentage (0\u2013100)',max:100,lowerBetter:false,grades:null}\n    };\n    const COUNTRY_PRESETS={none:'us4',us:'us4',pk:'us4',ca:'us43',uk:'percent',au:'au7',india:'cgpa10',germany:'de',saudi:'gulf5',uae:'gulf5'};\n    let currentSystem='us4';\n\n    el.innerHTML = `\n      <div class=\"gpa-shell\">\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Country \/ university preset<\/label>\n          <select id=\"gpa-country\">\n            <option value=\"none\">Custom (choose scale manually)<\/option>\n            <option value=\"us\">United States<\/option>\n            <option value=\"pk\">Pakistan (HEC)<\/option>\n            <option value=\"ca\">Canada<\/option>\n            <option value=\"uk\">United Kingdom<\/option>\n            <option value=\"au\">Australia<\/option>\n            <option value=\"india\">India<\/option>\n            <option value=\"germany\">Germany<\/option>\n            <option value=\"saudi\">Saudi Arabia<\/option>\n            <option value=\"uae\">UAE<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"field\"><label>Grading system<\/label>\n          <select id=\"gpa-system\">${Object.entries(GRADE_SYSTEMS).map(([k,v])=>`<option value=\"${k}\" ${k==='us4'?'selected':''}>${v.label}<\/option>`).join('')}<\/select>\n        <\/div>\n      <\/div>\n\n      <div id=\"gpa-rows\"><\/div>\n      <div class=\"inline-fields\" style=\"margin-bottom:16px;\">\n        <button class=\"btn btn-ghost btn-sm\" id=\"gpa-add\" type=\"button\">+ Add course<\/button>\n        <button class=\"btn btn-ghost btn-sm\" id=\"gpa-clear\" type=\"button\">Clear all<\/button>\n      <\/div>\n\n      <span class=\"more-link\" id=\"gpa-more-link\">+ Include previous semesters (cumulative GPA)<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"gpa-more-panel\">\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Previous CGPA<\/label><input type=\"number\" step=\"0.01\" id=\"gpa-prev-cgpa\" placeholder=\"e.g. 3.15\"><\/div>\n          <div class=\"field\"><label>Previous total credits<\/label><input type=\"number\" id=\"gpa-prev-credits\" placeholder=\"e.g. 60\"><\/div>\n        <\/div>\n        <div class=\"section-tag\">Graduation progress<\/div>\n        <div class=\"field\"><label>Total credits required to graduate<\/label><input type=\"number\" id=\"gpa-program-credits\" placeholder=\"e.g. 132\"><\/div>\n        <div class=\"section-tag\">Target GPA planner<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Desired final CGPA<\/label><input type=\"number\" step=\"0.01\" id=\"gpa-target\" placeholder=\"e.g. 3.50\"><\/div>\n          <div class=\"field\"><label>Remaining credit hours<\/label><input type=\"number\" id=\"gpa-remaining\" placeholder=\"e.g. 30\"><\/div>\n        <\/div>\n        <div class=\"section-tag\">Academic honors thresholds<\/div>\n        <div class=\"form-row\">\n          <div class=\"field\"><label>Dean's List<\/label><input type=\"number\" step=\"0.01\" id=\"gpa-hon-dean\" value=\"3.5\"><\/div>\n          <div class=\"field\"><label>Cum Laude<\/label><input type=\"number\" step=\"0.01\" id=\"gpa-hon-cum\" value=\"3.7\"><\/div>\n        <\/div>\n        <div class=\"field\"><label>Summa Cum Laude<\/label><input type=\"number\" step=\"0.01\" id=\"gpa-hon-summa\" value=\"3.9\"><\/div>\n        <div class=\"section-tag\">Scholarship eligibility<\/div>\n        <div class=\"field\"><label>Required GPA for scholarship<\/label><input type=\"number\" step=\"0.01\" id=\"gpa-scholarship\" placeholder=\"e.g. 3.5\"><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"gpa-btn\">Calculate GPA<\/button>\n\n      <div id=\"gpa-results\"><\/div>\n      <\/div>`;\n\n    function gradeOptionsHTML(){\n      const sys=GRADE_SYSTEMS[currentSystem];\n      if(!sys.grades) return null;\n      return Object.keys(sys.grades).map(g=>`<option ${g==='B'||g==='B+'||g==='2.0'||g==='6'?'selected':''}>${g}<\/option>`).join('');\n    }\n    function addRow(name,semester,category,credits,grade){\n      const row=document.createElement('div');\n      row.className='gpa-course-row';\n      const sys=GRADE_SYSTEMS[currentSystem];\n      const gradeCell = sys.grades\n        ? `<select class=\"gpa-grade\">${gradeOptionsHTML()}<\/select>`\n        : `<input type=\"number\" class=\"gpa-grade\" placeholder=\"%\" min=\"0\" max=\"100\" value=\"${grade!==undefined?grade:75}\">`;\n      row.innerHTML = `\n        <input type=\"text\" class=\"gpa-name\" placeholder=\"Course name\" value=\"${name||''}\">\n        <input type=\"text\" class=\"gpa-semester\" placeholder=\"Semester (e.g. Fall 2025)\" value=\"${semester||''}\">\n        <select class=\"gpa-category\">${['Major','Elective','Lab','General'].map(c=>`<option value=\"${c}\" ${c===(category||'Major')?'selected':''}>${c}<\/option>`).join('')}<\/select>\n        <input type=\"number\" class=\"gpa-credits\" placeholder=\"Credits\" value=\"${credits||3}\">\n        ${gradeCell}\n        <label class=\"gpa-mini-toggle\"><input type=\"checkbox\" class=\"gpa-repeat\"> Repeat<\/label>\n        <label class=\"gpa-mini-toggle\"><input type=\"checkbox\" class=\"gpa-pf\"> P\/F<\/label>\n        <button class=\"btn btn-ghost btn-sm\" type=\"button\" onclick=\"this.parentElement.remove()\">\u2715<\/button>`;\n      $('gpa-rows').appendChild(row);\n    }\n    function refreshGradeInputs(){\n      const sys=GRADE_SYSTEMS[currentSystem];\n      el.querySelectorAll('.gpa-course-row').forEach(row=>{\n        const oldCell=row.querySelector('.gpa-grade');\n        const wrap=document.createElement('div');\n        wrap.innerHTML = sys.grades ? `<select class=\"gpa-grade\">${gradeOptionsHTML()}<\/select>` : `<input type=\"number\" class=\"gpa-grade\" placeholder=\"%\" min=\"0\" max=\"100\" value=\"75\">`;\n        oldCell.replaceWith(wrap.firstElementChild);\n      });\n    }\n    addRow('Course 1','Semester 1','Major',3);\n    addRow('Course 2','Semester 1','Elective',4);\n    addRow('Course 3','Semester 2','Major',3);\n\n    $('gpa-country').onchange=(e)=>{\n      const preset=COUNTRY_PRESETS[e.target.value]||'us4';\n      $('gpa-system').value=preset; currentSystem=preset; refreshGradeInputs();\n    };\n    $('gpa-system').onchange=(e)=>{ currentSystem=e.target.value; refreshGradeInputs(); };\n    $('gpa-add').onclick=()=>addRow('','','Major',3);\n    $('gpa-clear').onclick=()=>{ $('gpa-rows').innerHTML=''; };\n    $('gpa-more-link').onclick=()=>$('gpa-more-panel').classList.toggle('show');\n\n    function computeAll(){\n      const sys=GRADE_SYSTEMS[currentSystem];\n      const rows=[...el.querySelectorAll('.gpa-course-row')];\n      const courses=rows.map((r,i)=>{\n        const name=r.querySelector('.gpa-name').value||('Course '+(i+1));\n        const semester=r.querySelector('.gpa-semester').value||'Semester';\n        const category=r.querySelector('.gpa-category').value;\n        const credits=+r.querySelector('.gpa-credits').value||0;\n        const isRepeat=r.querySelector('.gpa-repeat').checked;\n        const isPF=r.querySelector('.gpa-pf').checked;\n        const gradeRaw=r.querySelector('.gpa-grade').value;\n        let points, isFail, displayGrade;\n        if(sys.grades){\n          points=sys.grades[gradeRaw]; displayGrade=gradeRaw;\n          isFail = sys.lowerBetter ? points>=5 : points<=0;\n        } else {\n          const pct=+gradeRaw||0; points=(pct\/100)*sys.max; displayGrade=pct+'%';\n          isFail = pct<50;\n        }\n        return {name,semester,category,credits,points,isPF,isRepeat,isFail,displayGrade};\n      }).filter(c=>c.credits>0);\n      return {sys,courses};\n    }\n\n    $('gpa-btn').onclick=()=>{\n      const {sys,courses}=computeAll();\n      if(!courses.length){ $('gpa-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Add at least one course with credit hours.<\/div><\/div>'; return; }\n\n      const gpaCourses=courses.filter(c=>!c.isPF);\n      const totalPoints=gpaCourses.reduce((a,c)=>a+c.points*c.credits,0);\n      const totalCredits=gpaCourses.reduce((a,c)=>a+c.credits,0);\n      const semesterGPA = totalCredits>0 ? totalPoints\/totalCredits : 0;\n      const allCreditsAttempted = courses.reduce((a,c)=>a+c.credits,0);\n      const earnedCredits = courses.filter(c=>!c.isFail).reduce((a,c)=>a+c.credits,0);\n\n      const prevCGPA=+$('gpa-prev-cgpa').value||0, prevCredits=+$('gpa-prev-credits').value||0;\n      let finalCGPA=semesterGPA, combinedCredits=totalCredits;\n      if(prevCredits>0){\n        combinedCredits=prevCredits+totalCredits;\n        finalCGPA = combinedCredits>0 ? (prevCGPA*prevCredits + totalPoints)\/combinedCredits : semesterGPA;\n      }\n\n      const grades=gpaCourses.map(c=>c.points);\n      const best = sys.lowerBetter ? Math.min(...grades) : Math.max(...grades);\n      const worst = sys.lowerBetter ? Math.max(...grades) : Math.min(...grades);\n      const bestCourse=gpaCourses.find(c=>c.points===best), worstCourse=gpaCourses.find(c=>c.points===worst);\n\n      const majorCredits=courses.filter(c=>c.category==='Major').reduce((a,c)=>a+c.credits,0);\n      const electiveCredits=courses.filter(c=>c.category==='Elective').reduce((a,c)=>a+c.credits,0);\n      const labCredits=courses.filter(c=>c.category==='Lab').reduce((a,c)=>a+c.credits,0);\n      const repeatCount=courses.filter(c=>c.isRepeat).length;\n\n      const programCredits=+$('gpa-program-credits').value||0;\n      const gradProgress = programCredits>0 ? Math.min(100, combinedCredits\/programCredits*100) : null;\n\n      let insight;\n      const pctOfMax = sys.lowerBetter ? (1-(finalCGPA-1)\/(sys.max-1)) : (finalCGPA\/sys.max);\n      if(pctOfMax>=0.9) insight={text:'Excellent academic standing \u2014 likely competitive for many scholarships and graduate programs.',color:'#1E9E6B'};\n      else if(pctOfMax>=0.75) insight={text:'Solid performance. Raising one or two key high-credit course grades could meaningfully improve your CGPA.',color:'#0EA5A0'};\n      else if(pctOfMax>=0.55) insight={text:'On track, but there is room to improve. Focus on your highest-credit courses first, since they carry the most weight.',color:'#F5A623'};\n      else insight={text:'Consider focusing extra study time on your high-credit courses, since they have the largest impact on your overall GPA.',color:'#E0533D'};\n\n      \/\/ Trend by semester\n      const bySem={};\n      gpaCourses.forEach(c=>{ if(!bySem[c.semester]) bySem[c.semester]={pts:0,cr:0}; bySem[c.semester].pts+=c.points*c.credits; bySem[c.semester].cr+=c.credits; });\n      const semLabels=Object.keys(bySem);\n      const semGPAs=semLabels.map(s=>bySem[s].cr>0?(bySem[s].pts\/bySem[s].cr):0);\n\n      \/\/ Grade distribution\n      const dist={};\n      gpaCourses.forEach(c=>{ dist[c.displayGrade]=(dist[c.displayGrade]||0)+1; });\n\n      $('gpa-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${finalCGPA.toFixed(3)} \/ ${sys.max}<\/div><div class=\"dd-main-sub\">${prevCredits>0?'Cumulative GPA (CGPA)':'Semester GPA'} \u00b7 ${sys.label}<\/div><\/div>\n\n        <div class=\"gpa-insight\" style=\"background:${insight.color}18;border:1px solid ${insight.color}44;\">\n          <b style=\"color:${insight.color};\">Your insight:<\/b> ${insight.text}\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Academic dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Semester GPA<\/span><b>${semesterGPA.toFixed(3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Cumulative GPA<\/span><b>${finalCGPA.toFixed(3)}<\/b><\/div>\n            <div class=\"m-item\"><span>Total credits<\/span><b>${combinedCredits}<\/b><\/div>\n            <div class=\"m-item\"><span>Earned credits<\/span><b>${earnedCredits}<\/b><\/div>\n            <div class=\"m-item\"><span>Highest grade<\/span><b>${bestCourse?bestCourse.displayGrade:'\u2014'}<\/b><\/div>\n            <div class=\"m-item\"><span>Lowest grade<\/span><b>${worstCourse?worstCourse.displayGrade:'\u2014'}<\/b><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>Credit hour analysis<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Attempted credits<\/span><b>${allCreditsAttempted}<\/b><\/div>\n            <div class=\"m-item\"><span>Major credits<\/span><b>${majorCredits}<\/b><\/div>\n            <div class=\"m-item\"><span>Elective credits<\/span><b>${electiveCredits}<\/b><\/div>\n            <div class=\"m-item\"><span>Lab credits<\/span><b>${labCredits}<\/b><\/div>\n            <div class=\"m-item\"><span>Repeated courses<\/span><b>${repeatCount}<\/b><\/div>\n          <\/div>\n          ${gradProgress!==null?`\n          <div style=\"margin-top:16px;\">\n            <div style=\"display:flex;justify-content:space-between;font-size:.8rem;color:var(--text-soft);margin-bottom:6px;\"><span>Graduation progress<\/span><span>${combinedCredits} \/ ${programCredits} credits (${gradProgress.toFixed(0)}%)<\/span><\/div>\n            <div class=\"gpa-progress\"><div class=\"gpa-progress-fill\" style=\"width:${gradProgress}%;\"><\/div><\/div>\n          <\/div>`:''}\n        <\/div>\n\n        ${semLabels.length>1?`\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>GPA trend by semester<\/h5><\/div>\n          <div class=\"gpa-bar-chart\">${semLabels.map((s,i)=>`<div class=\"bar-col\"><div class=\"bar-val\">${semGPAs[i].toFixed(2)}<\/div><div class=\"bar\" style=\"height:${Math.max(4,semGPAs[i]\/sys.max*130)}px;\"><\/div><div class=\"bar-label\">${s}<\/div><\/div>`).join('')}<\/div>\n        <\/div>`:''}\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Grade distribution<\/h5><\/div>\n          <div class=\"gpa-bar-chart\">${Object.keys(dist).map(g=>`<div class=\"bar-col\"><div class=\"bar-val\">${dist[g]}<\/div><div class=\"bar\" style=\"height:${Math.max(4,dist[g]\/Math.max(...Object.values(dist))*130)}px;background:var(--amber);\"><\/div><div class=\"bar-label\">${g}<\/div><\/div>`).join('')}<\/div>\n        <\/div>\n\n        ${!sys.lowerBetter?`\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('grad',16)}<\/div><h5>Academic honors predictor<\/h5><\/div>\n          <table class=\"ref-table\"><tbody>\n            <tr><td>Dean's List (${$('gpa-hon-dean').value})<\/td><td>${finalCGPA>=(+$('gpa-hon-dean').value)?'\u2705 Eligible':'Not yet'}<\/td><\/tr>\n            <tr><td>Cum Laude (${$('gpa-hon-cum').value})<\/td><td>${finalCGPA>=(+$('gpa-hon-cum').value)?'\u2705 Eligible':'Not yet'}<\/td><\/tr>\n            <tr><td>Summa Cum Laude (${$('gpa-hon-summa').value})<\/td><td>${finalCGPA>=(+$('gpa-hon-summa').value)?'\u2705 Eligible':'Not yet'}<\/td><\/tr>\n          <\/tbody><\/table>\n        <\/div>`:''}\n\n        ${$('gpa-scholarship').value?`\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('coins',16)}<\/div><h5>Scholarship eligibility<\/h5><\/div>\n          ${(()=>{ const req=+$('gpa-scholarship').value; const met = sys.lowerBetter ? finalCGPA<=req : finalCGPA>=req; const gap=Math.abs(req-finalCGPA).toFixed(2);\n            return `<div class=\"summary-mini\"><div class=\"m-item\"><span>Required GPA<\/span><b>${req}<\/b><\/div><div class=\"m-item\"><span>Your GPA<\/span><b>${finalCGPA.toFixed(2)}<\/b><\/div><div class=\"m-item\"><span>Status<\/span><b>${met?'\u2705 Eligible':'Need +'+gap}<\/b><\/div><\/div>`; })()}\n        <\/div>`:''}\n\n        ${(+$('gpa-target').value && +$('gpa-remaining').value)?`\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Target GPA planner<\/h5><\/div>\n          ${(()=>{ const target=+$('gpa-target').value, remaining=+$('gpa-remaining').value;\n            const neededPoints = target*(combinedCredits+remaining) - (finalCGPA*combinedCredits);\n            const neededGPA = neededPoints\/remaining;\n            const feasible = sys.lowerBetter ? neededGPA>=1 : neededGPA<=sys.max;\n            return `<p style=\"color:var(--text-soft);font-size:.88rem;\">To reach a CGPA of <b>${target}<\/b> after ${remaining} more credit hours, you need to average:<\/p><div class=\"dd-main-result\" style=\"margin-top:10px;\"><div class=\"dd-main-value\">${feasible?neededGPA.toFixed(2):'Not achievable'}<\/div><div class=\"dd-main-sub\">${feasible?'Required GPA in remaining credits':'Would require exceeding the scale \u2014 consider a lower target or more credits'}<\/div><\/div>`; })()}\n        <\/div>`:''}\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>Quick GPA \u2194 Percentage conversion<\/h5><\/div>\n          <div class=\"form-row\">\n            <div class=\"field\"><label>GPA value<\/label><input type=\"number\" step=\"0.01\" id=\"gpa-conv-gpa\" value=\"${finalCGPA.toFixed(2)}\"><\/div>\n            <div class=\"field\"><label>\u2248 Percentage<\/label><input type=\"number\" id=\"gpa-conv-pct\" readonly><\/div>\n          <\/div>\n          <p style=\"font-size:.75rem;color:var(--text-soft);\">Approximate linear conversion (GPA \u00f7 scale max \u00d7 100) \u2014 universities may use different official conversion tables.<\/p>\n        <\/div>\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"gpa-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"gpa-print\" type=\"button\">Print<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"gpa-csv\" type=\"button\">Export CSV<\/button>\n          <\/div>\n        <\/div>`;\n\n      const convGpa=$('gpa-conv-gpa'), convPct=$('gpa-conv-pct');\n      function updateConv(){ convPct.value=(((+convGpa.value)\/sys.max)*100).toFixed(1); }\n      updateConv(); convGpa.oninput=updateConv;\n\n      const shareText=`My ${prevCredits>0?'CGPA':'GPA'} is ${finalCGPA.toFixed(3)} out of ${sys.max} across ${combinedCredits} credit hours. Calculated on MAFtrix.`;\n      $('gpa-copy').onclick=()=>{ const done=()=>{$('gpa-copy').textContent='Copied!';setTimeout(()=>$('gpa-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(done).catch(done):done(); };\n      $('gpa-print').onclick=()=>window.print();\n      $('gpa-csv').onclick=()=>{\n        let csv='Course,Semester,Category,Credits,Grade\\n';\n        courses.forEach(c=>csv+=`${c.name},${c.semester},${c.category},${c.credits},${c.displayGrade}\\n`);\n        csv+=`\\nSemester GPA,${semesterGPA.toFixed(3)}\\nCumulative GPA,${finalCGPA.toFixed(3)}\\nTotal Credits,${combinedCredits}\\n`;\n        const blob=new Blob([csv],{type:'text\/csv'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='gpa-report.csv'; a.click(); URL.revokeObjectURL(url);\n      };\n    };\n  }});\n\nreg({slug:'grade',name:'Grade Calculator',cat:'everyday',icn:'grad',photo:40,\n  desc:'A complete grade planning tool: weighted or points-based grading, letter grade and GPA conversion, goal planning, what-if simulator, and pass\/fail prediction.',\n  render(el){\n    const LETTER_SCALES={\n      us_pm:{label:'US (with +\/-)',bands:[[97,'A+',4.0],[93,'A',4.0],[90,'A-',3.7],[87,'B+',3.3],[83,'B',3.0],[80,'B-',2.7],[77,'C+',2.3],[73,'C',2.0],[70,'C-',1.7],[67,'D+',1.3],[60,'D',1.0],[0,'F',0]]},\n      us_simple:{label:'US (simple A-F)',bands:[[90,'A',4.0],[80,'B',3.0],[70,'C',2.0],[60,'D',1.0],[0,'F',0]]},\n      pk_hec:{label:'Pakistan (HEC)',bands:[[85,'A',4.0],[80,'A-',3.7],[75,'B+',3.3],[71,'B',3.0],[68,'B-',2.7],[64,'C+',2.3],[61,'C',2.0],[58,'C-',1.7],[54,'D+',1.3],[50,'D',1.0],[0,'F',0]]},\n      uk:{label:'United Kingdom',bands:[[70,'First',4.0],[60,'2:1 Upper Second',3.3],[50,'2:2 Lower Second',2.7],[40,'Third',2.0],[0,'Fail',0]]},\n      au:{label:'Australia',bands:[[85,'HD (High Distinction)',7],[75,'D (Distinction)',6],[65,'C (Credit)',5],[50,'P (Pass)',4],[0,'F (Fail)',0]]}\n    };\n    function letterOf(pct,scaleKey){\n      const bands=LETTER_SCALES[scaleKey].bands;\n      for(const [min,letter,gp] of bands){ if(pct>=min) return {letter,gp}; }\n      return {letter:bands[bands.length-1][1],gp:bands[bands.length-1][2]};\n    }\n    let inputMode='weighted';\n\n    el.innerHTML = `\n      <div class=\"grade-shell\">\n      <div class=\"seg\" id=\"gr-mode-seg\">\n        <button class=\"active\" data-v=\"weighted\">Weighted %<\/button>\n        <button data-v=\"points\">Points-based<\/button>\n      <\/div>\n      <div class=\"field\"><label>Grading scale (for letter grade &amp; GPA)<\/label>\n        <select id=\"gr-scale\">${Object.entries(LETTER_SCALES).map(([k,v])=>`<option value=\"${k}\" ${k==='us_pm'?'selected':''}>${v.label}<\/option>`).join('')}<\/select>\n      <\/div>\n\n      <div id=\"gr-rows\"><\/div>\n      <button class=\"btn btn-ghost btn-sm\" id=\"gr-add\" type=\"button\" style=\"margin-bottom:16px;\">+ Add assessment<\/button>\n\n      <div class=\"form-row\">\n        <div class=\"field\"><label>Target overall grade (%)<\/label><input type=\"number\" id=\"gr-target\" value=\"90\"><\/div>\n        <div class=\"field\"><label>Passing grade (%)<\/label><input type=\"number\" id=\"gr-pass\" value=\"60\"><\/div>\n      <\/div>\n      <span class=\"more-link\" id=\"gr-more-link\">+ Compare to class average<\/span>\n      <div class=\"adv-panel more-panel-card\" id=\"gr-more-panel\">\n        <div class=\"field\"><label>Class average (%) <small>optional<\/small><\/label><input type=\"number\" id=\"gr-classavg\" placeholder=\"e.g. 76\"><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"gr-btn\">Calculate grade<\/button>\n\n      <div id=\"gr-results\"><\/div>\n      <\/div>`;\n\n    function addRow(name,weight,score,obtained,total){\n      const row=document.createElement('div');\n      row.className='row-add';\n      row.dataset.weighted = `<input type=\"text\" placeholder=\"Assessment\" value=\"${name||''}\" class=\"gr-name\">\n        <input type=\"number\" placeholder=\"Weight %\" value=\"${weight!==undefined?weight:20}\" style=\"max-width:90px;\" class=\"gr-weight\">\n        <input type=\"number\" placeholder=\"Score % (blank = not done)\" value=\"${score!==undefined?score:''}\" style=\"max-width:150px;\" class=\"gr-score\">`;\n      row.dataset.points = `<input type=\"text\" placeholder=\"Assessment\" value=\"${name||''}\" class=\"gr-name\">\n        <input type=\"number\" placeholder=\"Obtained (blank = not done)\" value=\"${obtained!==undefined?obtained:''}\" style=\"max-width:130px;\" class=\"gr-obtained\">\n        <input type=\"number\" placeholder=\"Total points\" value=\"${total!==undefined?total:100}\" style=\"max-width:110px;\" class=\"gr-total\">`;\n      row.innerHTML = (inputMode==='weighted'?row.dataset.weighted:row.dataset.points) + `<button class=\"btn btn-ghost btn-sm\" type=\"button\" onclick=\"this.parentElement.remove()\">\u2715<\/button>`;\n      $('gr-rows').appendChild(row);\n    }\n    function rebuildRowsForMode(){\n      el.querySelectorAll('#gr-rows .row-add').forEach(row=>{\n        const name=row.querySelector('.gr-name')?.value||'';\n        const btn=row.querySelector('button');\n        row.innerHTML = (inputMode==='weighted'?row.dataset.weighted:row.dataset.points);\n        row.querySelector('.gr-name').value=name;\n        row.appendChild(btn);\n      });\n    }\n    addRow('Homework',20,88,88,100); addRow('Midterm',30,84,84,100); addRow('Final exam',30,'',undefined,100);\n    $('gr-add').onclick=()=>addRow('',10,'',undefined,100);\n    $('gr-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('gr-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      inputMode=b.dataset.v; rebuildRowsForMode();\n    });\n    $('gr-more-link').onclick=()=>$('gr-more-panel').classList.toggle('show');\n\n    function computeAssessments(){\n      const rows=[...el.querySelectorAll('#gr-rows .row-add')];\n      if(inputMode==='weighted'){\n        const totalW=rows.reduce((a,r)=>a+(+r.querySelector('.gr-weight').value||0),0);\n        return rows.map((r,i)=>{\n          const name=r.querySelector('.gr-name').value||('Item '+(i+1));\n          const w=+r.querySelector('.gr-weight').value||0;\n          const scoreStr=r.querySelector('.gr-score').value;\n          return {name, weightFrac: totalW>0?w\/totalW:0, weightPct:w, score: scoreStr!==''?+scoreStr:null};\n        });\n      } else {\n        const totalPts=rows.reduce((a,r)=>a+(+r.querySelector('.gr-total').value||0),0);\n        return rows.map((r,i)=>{\n          const name=r.querySelector('.gr-name').value||('Item '+(i+1));\n          const total=+r.querySelector('.gr-total').value||0;\n          const obtStr=r.querySelector('.gr-obtained').value;\n          return {name, weightFrac: totalPts>0?total\/totalPts:0, weightPct: totalPts>0?(total\/totalPts*100):0, score: obtStr!==''?(+obtStr\/total*100):null};\n        });\n      }\n    }\n\n    $('gr-btn').onclick=()=>{\n      const assessments=computeAssessments();\n      const totalWeight=assessments.reduce((a,c)=>a+c.weightFrac,0)*100;\n      if(totalWeight<=0){ $('gr-results').innerHTML='<div class=\"readout error\"><div class=\"r-label\">Check your inputs<\/div><div class=\"r-value\">Add at least one assessment.<\/div><\/div>'; return; }\n\n      const done=assessments.filter(c=>c.score!==null);\n      const pending=assessments.filter(c=>c.score===null);\n      const earnedWeight=done.reduce((a,c)=>a+c.weightFrac*c.score,0);\n      const completedWeight=done.reduce((a,c)=>a+c.weightFrac,0)*100;\n      const remainingWeight=100-completedWeight;\n      const currentGrade = completedWeight>0 ? (earnedWeight\/(completedWeight\/100)) : 0;\n\n      const scaleKey=$('gr-scale').value;\n      const {letter,gp}=letterOf(currentGrade,scaleKey);\n      const target=+$('gr-target').value||0;\n      const passMark=+$('gr-pass').value||0;\n      const classAvg=+$('gr-classavg').value||0;\n\n      const scores=done.map(c=>c.score);\n      const avgScore=scores.length?scores.reduce((a,b)=>a+b,0)\/scores.length:0;\n      const highest=scores.length?Math.max(...scores):null;\n      const lowest=scores.length?Math.min(...scores):null;\n\n      let neededOnRemaining=null, feasible=true;\n      if(remainingWeight>0){\n        const neededTotalPoints=target;\n        neededOnRemaining=(neededTotalPoints-earnedWeight)\/(remainingWeight\/100);\n        feasible = neededOnRemaining<=100;\n      }\n\n      \/\/ best-case \/ worst-case projection assuming remaining all 100 or all 0\n      const bestCase = earnedWeight + remainingWeight;\n      const worstCase = earnedWeight;\n\n      let insight;\n      if(currentGrade>=90) insight={text:'Excellent performance \u2014 focus on maintaining consistency through the remaining assessments.',color:'#1E9E6B'};\n      else if(currentGrade>=80) insight={text:'Strong results. A small improvement on a major upcoming assessment could move you into the next grade band.',color:'#0EA5A0'};\n      else if(currentGrade>=70) insight={text:'You are performing reasonably well. Review your lower-scoring assignments to find quick improvement opportunities.',color:'#F5A623'};\n      else insight={text:'Prioritize your highest-weight upcoming assessments, and consider seeking help on topics behind your lower scores.',color:'#E0533D'};\n\n      \/\/ Improvement suggestion: find highest-weight pending item, compute score needed to move up one letter band\n      let improvementTip='';\n      if(pending.length){\n        const biggest=pending.reduce((a,b)=>a.weightFrac>b.weightFrac?a:b);\n        const bands=LETTER_SCALES[scaleKey].bands;\n        const currentBandIdx=bands.findIndex(b=>currentGrade>=b[0]);\n        if(currentBandIdx>0){\n          const nextBand=bands[currentBandIdx-1];\n          const neededForNext=(nextBand[0]-earnedWeight)\/(remainingWeight\/100);\n          if(neededForNext<=100){\n            improvementTip=`Scoring ${num(neededForNext,0)}% on \"${biggest.name}\" (your highest-weight remaining assessment) could raise your grade from ${letter} to ${nextBand[1]}.`;\n          }\n        }\n      }\n\n      $('gr-results').innerHTML = `\n        <div class=\"dd-main-result\"><div class=\"dd-main-value\">${num(currentGrade,1)}% \u00b7 ${letter}<\/div><div class=\"dd-main-sub\">GPA equivalent: ${gp.toFixed(1)} \u00b7 ${LETTER_SCALES[scaleKey].label}<\/div><\/div>\n\n        <div class=\"gpa-insight\" style=\"background:${insight.color}18;border:1px solid ${insight.color}44;\">\n          <b style=\"color:${insight.color};\">Your insight:<\/b> ${insight.text}\n        <\/div>\n        ${improvementTip?`<div class=\"gpa-insight\" style=\"background:#4C6FFF18;border:1px solid #4C6FFF44;margin-top:10px;\"><b style=\"color:#4C6FFF;\">Improvement tip:<\/b> ${improvementTip}<\/div>`:''}\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Grade breakdown dashboard<\/h5><\/div>\n          <div class=\"summary-mini\">\n            <div class=\"m-item\"><span>Current grade<\/span><b>${num(currentGrade,1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Target grade<\/span><b>${target}%<\/b><\/div>\n            <div class=\"m-item\"><span>Average score<\/span><b>${num(avgScore,1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Highest score<\/span><b>${highest!==null?num(highest,1)+'%':'\u2014'}<\/b><\/div>\n            <div class=\"m-item\"><span>Lowest score<\/span><b>${lowest!==null?num(lowest,1)+'%':'\u2014'}<\/b><\/div>\n            <div class=\"m-item\"><span>Remaining weight<\/span><b>${num(remainingWeight,1)}%<\/b><\/div>\n          <\/div>\n          <div style=\"margin-top:16px;\">\n            <div style=\"display:flex;justify-content:space-between;font-size:.8rem;color:var(--text-soft);margin-bottom:6px;\"><span>Course completion<\/span><span>${num(completedWeight,0)}% graded<\/span><\/div>\n            <div class=\"gpa-progress\"><div class=\"gpa-progress-fill\" style=\"width:${completedWeight}%;\"><\/div><\/div>\n          <\/div>\n        <\/div>\n\n        ${remainingWeight>0?`\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('percent',16)}<\/div><h5>Goal grade planner<\/h5><\/div>\n          <p style=\"color:var(--text-soft);font-size:.88rem;\">To reach <b>${target}%<\/b> overall, you need to average on the remaining ${num(remainingWeight,1)}% of your grade:<\/p>\n          <div class=\"dd-main-result\" style=\"margin-top:10px;\"><div class=\"dd-main-value\">${feasible?num(neededOnRemaining,1)+'%':'Not achievable'}<\/div><div class=\"dd-main-sub\">${feasible?'Required average on remaining assessments':'Would require exceeding 100% \u2014 consider a lower target'}<\/div><\/div>\n          <div class=\"summary-mini\" style=\"margin-top:14px;\">\n            <div class=\"m-item\"><span>Best case (100% remaining)<\/span><b>${num(bestCase,1)}%<\/b><\/div>\n            <div class=\"m-item\"><span>Worst case (0% remaining)<\/span><b>${num(worstCase,1)}%<\/b><\/div>\n          <\/div>\n        <\/div>`:''}\n\n        <div class=\"calc-section section-amber\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>Pass\/fail predictor<\/h5><\/div>\n          ${(()=>{ const projected = remainingWeight>0 ? (earnedWeight+remainingWeight*(avgScore\/100)) : currentGrade;\n            const likely = projected>=passMark;\n            return `<div class=\"summary-mini\"><div class=\"m-item\"><span>Passing requirement<\/span><b>${passMark}%<\/b><\/div><div class=\"m-item\"><span>Current projection<\/span><b>${num(projected,1)}%<\/b><\/div><div class=\"m-item\"><span>Status<\/span><b>${likely?'\u2705 Likely to pass':'\u26a0 At risk'}<\/b><\/div><\/div><p style=\"font-size:.75rem;color:var(--text-soft);margin-top:10px;\">Projection assumes your average score continues on remaining assessments \u2014 not a guarantee.<\/p>`; })()}\n        <\/div>\n\n        ${classAvg>0?`\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Class comparison<\/h5><\/div>\n          <div class=\"summary-mini\"><div class=\"m-item\"><span>Class average<\/span><b>${classAvg}%<\/b><\/div><div class=\"m-item\"><span>Your grade<\/span><b>${num(currentGrade,1)}%<\/b><\/div><div class=\"m-item\"><span>Standing<\/span><b>${currentGrade>classAvg+2?'Above average':currentGrade<classAvg-2?'Below average':'Around average'}<\/b><\/div><\/div>\n        <\/div>`:''}\n\n        <div class=\"calc-section section-blue\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Assessment scores &amp; weight distribution<\/h5><\/div>\n          <div class=\"gpa-bar-chart\">${assessments.map(a=>`<div class=\"bar-col\"><div class=\"bar-val\">${a.score!==null?num(a.score,0)+'%':'\u2014'}<\/div><div class=\"bar\" style=\"height:${a.score!==null?Math.max(4,a.score\/100*130):4}px;\"><\/div><div class=\"bar-label\">${a.name}<br><small style=\"color:var(--text-soft);\">${num(a.weightPct,0)}%<\/small><\/div><\/div>`).join('')}<\/div>\n        <\/div>\n\n        <div class=\"calc-section section-green\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('equation',16)}<\/div><h5>What-if simulator<\/h5><\/div>\n          <div class=\"field\"><label>Choose an assessment<\/label><select id=\"gr-sim-select\">${assessments.map((a,i)=>`<option value=\"${i}\">${a.name}<\/option>`).join('')}<\/select><\/div>\n          <div class=\"field\"><label>Hypothetical score: <span id=\"gr-sim-val\">75<\/span>%<\/label><input type=\"range\" id=\"gr-sim-slider\" min=\"0\" max=\"100\" value=\"75\"><\/div>\n          <div id=\"gr-sim-result\" class=\"summary-mini\"><\/div>\n        <\/div>\n\n        <div class=\"calc-section section-navy\">\n          <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n          <div class=\"form-row\">\n            <button class=\"btn btn-ghost btn-block\" id=\"gr-copy\" type=\"button\">Copy result<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"gr-print\" type=\"button\">Print<\/button>\n            <button class=\"btn btn-ghost btn-block\" id=\"gr-csv\" type=\"button\">Export CSV<\/button>\n          <\/div>\n        <\/div>`;\n\n      function simUpdate(){\n        const idx=+$('gr-sim-select').value, hypScore=+$('gr-sim-slider').value;\n        $('gr-sim-val').textContent=hypScore;\n        let simEarned=0;\n        assessments.forEach((a,i)=>{\n          const s = i===idx ? hypScore : (a.score!==null?a.score:avgScore);\n          simEarned += a.weightFrac*s;\n        });\n        const {letter:simLetter}=letterOf(simEarned,scaleKey);\n        $('gr-sim-result').innerHTML = `<div class=\"m-item\"><span>Simulated final grade<\/span><b>${num(simEarned,1)}%<\/b><\/div><div class=\"m-item\"><span>Letter grade<\/span><b>${simLetter}<\/b><\/div>`;\n      }\n      $('gr-sim-select').onchange=simUpdate; $('gr-sim-slider').oninput=simUpdate; simUpdate();\n\n      const shareText=`My current grade is ${num(currentGrade,1)}% (${letter}), targeting ${target}%. Calculated on MAFtrix.`;\n      $('gr-copy').onclick=()=>{ const doneFn=()=>{$('gr-copy').textContent='Copied!';setTimeout(()=>$('gr-copy').textContent='Copy result',1500);}; navigator.clipboard?navigator.clipboard.writeText(shareText).then(doneFn).catch(doneFn):doneFn(); };\n      $('gr-print').onclick=()=>window.print();\n      $('gr-csv').onclick=()=>{\n        let csv='Assessment,Weight %,Score %\\n';\n        assessments.forEach(a=>csv+=`${a.name},${a.weightPct.toFixed(1)},${a.score!==null?a.score.toFixed(1):''}\\n`);\n        csv+=`\\nCurrent Grade,${currentGrade.toFixed(1)}\\nLetter Grade,${letter}\\nGPA Equivalent,${gp}\\n`;\n        const blob=new Blob([csv],{type:'text\/csv'}); const url=URL.createObjectURL(blob);\n        const a=document.createElement('a'); a.href=url; a.download='grade-report.csv'; a.click(); URL.revokeObjectURL(url);\n      };\n    };\n  }});\n\nreg({slug:'password',name:'Password Generator',cat:'everyday',icn:'lock',photo:41,\n  desc:'Generate strong random, pronounceable, passphrase, PIN, WiFi or business-grade passwords with a strength meter, crack-time estimate and breach check.',\n  render(el){\n    const PASSPHRASE_WORDS=['apple','river','tiger','cloud','stone','flame','ocean','maple','shadow','comet','breeze','falcon','garden','harbor','island','jungle','kettle','lantern','meadow','nebula','orchid','pepper','quartz','rabbit','summit','thunder','umbrella','velvet','willow','xenon','yellow','zephyr','anchor','basil','canyon','desert','ember','forest','glacier','horizon','ivory','jasper','kernel','lagoon','mantle','nectar','opal','pyramid','quiver','ridge','sable','timber','urchin','valley','walnut','yonder','zenith','amber','birch','copper','dune','echo','fjord','granite','hollow','iris','jade','knoll','lunar','marble','nomad','onyx','plaza','quest','raven','solar','trail','utopia','vertex','wharf','yield','zodiac','arbor','blaze','crest','delta','ferry','grove','haven','ingot','joust','karma','lodge','mango','north','oasis','pilot','quilt','robin','shore','tempo','unity','vapor','wagon','yacht'];\n    const WEAK_PASSWORDS=['123456','password','123456789','qwerty','abc123','letmein','monkey','football','iloveyou','admin'];\n    let history=[];\n\n    function formatCrackTime(seconds){\n      const YEAR=31557600;\n      const units=[['second',1],['minute',60],['hour',3600],['day',86400],['year',YEAR],['thousand years',YEAR*1e3],['million years',YEAR*1e6],['billion years',YEAR*1e9],['trillion years',YEAR*1e12],['quadrillion years',YEAR*1e15]];\n      if(seconds<1) return 'Instantly';\n      for(let i=units.length-1;i>=0;i--){\n        if(seconds>=units[i][1]){\n          const val=seconds\/units[i][1];\n          const label=units[i][0];\n          const plural = label.endsWith('s') ? label : (val===1?label:label+'s');\n          return (val>=1000?val.toExponential(1):val.toFixed(val<10?1:0))+' '+plural;\n        }\n      }\n      return seconds.toFixed(1)+' seconds';\n    }\n    function poolSizeFor(opts){\n      let n=0;\n      if(opts.upper) n+=opts.avoid?24:26;\n      if(opts.lower) n+=opts.avoid?24:26;\n      if(opts.num) n+=opts.avoid?8:10;\n      if(opts.sym) n+=12;\n      if(opts.space) n+=1;\n      return n;\n    }\n    function strengthOf(entropyBits){\n      if(entropyBits<28) return {label:'Weak',color:'#E0533D',pct:20};\n      if(entropyBits<36) return {label:'Fair',color:'#F5A623',pct:40};\n      if(entropyBits<60) return {label:'Good',color:'#F5C542',pct:65};\n      if(entropyBits<128) return {label:'Strong',color:'#1E9E6B',pct:88};\n      return {label:'Very Strong',color:'#0EA5A0',pct:100};\n    }\n    function randChar(str){ return str[Math.floor(Math.random()*str.length)]; }\n    function genRandom(len,opts){\n      let chars='';\n      if(opts.upper) chars+= opts.avoid?'ABCDEFGHJKLMNPQRSTUVWXYZ':'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n      if(opts.lower) chars+= opts.avoid?'abcdefghijkmnpqrstuvwxyz':'abcdefghijklmnopqrstuvwxyz';\n      if(opts.num) chars+= opts.avoid?'23456789':'0123456789';\n      if(opts.sym) chars+='!@#$%^&#038;*-_=+?';\n      if(opts.space) chars+=' ';\n      if(!chars) return null;\n      let pass=''; for(let i=0;i<len;i++) pass+=randChar(chars);\n      return pass;\n    }\n    function genPronounceable(targetLen){\n      const cons='bcdfghjklmnpqrstvwxyz', vow='aeiou';\n      let word='';\n      while(word.length<targetLen-2){ word+=randChar(cons)+randChar(vow); }\n      word = word.charAt(0).toUpperCase()+word.slice(1);\n      word += Math.floor(Math.random()*90+10) + randChar('!@#$%');\n      return word;\n    }\n    function genPassphrase(wordCount,sep,capitalize,addNumber){\n      const words=[];\n      for(let i=0;i<wordCount;i++){\n        let w=PASSPHRASE_WORDS[Math.floor(Math.random()*PASSPHRASE_WORDS.length)];\n        if(capitalize) w=w.charAt(0).toUpperCase()+w.slice(1);\n        words.push(w);\n      }\n      if(addNumber) words.push(String(Math.floor(Math.random()*90+10)));\n      return words.join(sep);\n    }\n    function genPIN(len){ let p=''; for(let i=0;i<len;i++) p+=Math.floor(Math.random()*10); return p; }\n\n    el.innerHTML = `\n      <div class=\"pw-shell\">\n      <div class=\"seg\" id=\"pw-mode-seg\">\n        <button class=\"active\" data-v=\"random\">Random<\/button>\n        <button data-v=\"pronounceable\">Pronounceable<\/button>\n        <button data-v=\"passphrase\">Passphrase<\/button>\n        <button data-v=\"pin\">PIN<\/button>\n        <button data-v=\"wifi\">WiFi<\/button>\n        <button data-v=\"business\">Business<\/button>\n      <\/div>\n\n      <div id=\"pw-random-fields\">\n        <div class=\"field\"><label>Length: <span id=\"pw-len-val\">16<\/span><\/label><input type=\"range\" id=\"pw-len\" min=\"4\" max=\"128\" value=\"16\"><\/div>\n        <div class=\"inline-fields\" style=\"margin-bottom:10px;\">\n          <button class=\"tag-btn active\" data-k=\"upper\">A-Z<\/button>\n          <button class=\"tag-btn active\" data-k=\"lower\">a-z<\/button>\n          <button class=\"tag-btn active\" data-k=\"num\">0-9<\/button>\n          <button class=\"tag-btn active\" data-k=\"sym\">!@#$<\/button>\n          <button class=\"tag-btn\" data-k=\"space\">Spaces<\/button>\n        <\/div>\n        <label class=\"adv-toggle\"><input type=\"checkbox\" id=\"pw-avoid\"> Avoid similar characters (O\/0, l\/1\/I)<\/label>\n      <\/div>\n\n      <div id=\"pw-passphrase-fields\" hidden>\n        <div class=\"field\"><label>Number of words: <span id=\"pw-words-val\">4<\/span><\/label><input type=\"range\" id=\"pw-words\" min=\"3\" max=\"8\" value=\"4\"><\/div>\n        <div class=\"field\"><label>Separator<\/label><select id=\"pw-sep\"><option value=\"-\">Hyphen (-)<\/option><option value=\"_\">Underscore (_)<\/option><option value=\".\">Period (.)<\/option><option value=\" \">Space<\/option><\/select><\/div>\n        <label class=\"adv-toggle\"><input type=\"checkbox\" id=\"pw-cap\" checked> Capitalize each word<\/label>\n        <label class=\"adv-toggle\"><input type=\"checkbox\" id=\"pw-num2\" checked> Add a number<\/label>\n      <\/div>\n\n      <div id=\"pw-pin-fields\" hidden>\n        <div class=\"field\"><label>PIN length<\/label><select id=\"pw-pin-len\"><option value=\"4\">4 digits<\/option><option value=\"6\" selected>6 digits<\/option><option value=\"8\">8 digits<\/option><\/select><\/div>\n      <\/div>\n\n      <button class=\"btn btn-primary btn-block\" id=\"pw-btn\">Generate password<\/button>\n\n      <div class=\"pw-terminal\" id=\"pw-terminal-box\" hidden>\n        <div class=\"pw-value\" id=\"pw-value\"><\/div>\n        <div class=\"pw-actions\">\n          <button class=\"btn btn-amber btn-sm\" id=\"pw-copy\" type=\"button\">Copy<\/button>\n          <button class=\"btn btn-outline btn-sm\" id=\"pw-download\" type=\"button\">Download .txt<\/button>\n          <button class=\"btn btn-outline btn-sm\" id=\"pw-breach\" type=\"button\">Check for breaches<\/button>\n        <\/div>\n        <div id=\"pw-breach-result\" style=\"margin-top:12px;font-size:.85rem;color:rgba(255,255,255,.75);\"><\/div>\n      <\/div>\n\n      <div class=\"strength-wrap\" id=\"pw-strength-wrap\" hidden>\n        <div class=\"strength-label\"><span id=\"pw-strength-text\"><\/span><span id=\"pw-strength-pct\"><\/span><\/div>\n        <div class=\"strength-bar\"><div class=\"strength-fill\" id=\"pw-strength-fill\"><\/div><\/div>\n        <div class=\"summary-mini\" id=\"pw-meta\" style=\"margin-top:12px;\"><\/div>\n        <div id=\"pw-advice\" style=\"margin-top:10px;font-size:.85rem;color:var(--text-soft);\"><\/div>\n      <\/div>\n\n      <div id=\"pw-dist\" style=\"margin-top:18px;\"><\/div>\n\n      <div class=\"privacy-note\">\ud83d\udd12 Passwords are generated entirely in your browser and are never sent to or stored on any server.<\/div>\n      <\/div>\n\n      <div class=\"calc-section section-navy\" style=\"margin-top:38px;\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('lock',16)}<\/div><h5>Famous weak passwords (never use these)<\/h5><\/div>\n        <table class=\"ref-table weak-pw-table\"><thead><tr><th>Password<\/th><th>Why it's weak<\/th><\/tr><\/thead><tbody>\n          <tr><td>123456<\/td><td>Sequential digits \u2014 the single most common leaked password<\/td><\/tr>\n          <tr><td>password<\/td><td>Literal dictionary word, guessed in milliseconds<\/td><\/tr>\n          <tr><td>qwerty<\/td><td>Keyboard pattern, trivially guessable<\/td><\/tr>\n          <tr><td>iloveyou<\/td><td>Common phrase found in every cracking dictionary<\/td><\/tr>\n          <tr><td>admin<\/td><td>Default credential guessed by automated bots constantly<\/td><\/tr>\n        <\/tbody><\/table>\n      <\/div>\n\n      <div class=\"calc-section section-amber\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('activity',16)}<\/div><h5>How hackable are you? (quick quiz)<\/h5><\/div>\n        <div class=\"quiz-q\"><p>Do you reuse the same password across multiple sites?<\/p><div class=\"quiz-opts\" data-q=\"reuse\"><button class=\"quiz-opt\" data-v=\"0\">Never<\/button><button class=\"quiz-opt\" data-v=\"1\">Sometimes<\/button><button class=\"quiz-opt\" data-v=\"2\">Always<\/button><\/div><\/div>\n        <div class=\"quiz-q\"><p>How long are your typical passwords?<\/p><div class=\"quiz-opts\" data-q=\"length\"><button class=\"quiz-opt\" data-v=\"0\">12+ characters<\/button><button class=\"quiz-opt\" data-v=\"1\">8-12 characters<\/button><button class=\"quiz-opt\" data-v=\"2\">Under 8<\/button><\/div><\/div>\n        <div class=\"quiz-q\"><p>Do you use two-factor authentication (2FA)?<\/p><div class=\"quiz-opts\" data-q=\"tfa\"><button class=\"quiz-opt\" data-v=\"0\">Always<\/button><button class=\"quiz-opt\" data-v=\"1\">Sometimes<\/button><button class=\"quiz-opt\" data-v=\"2\">Never<\/button><\/div><\/div>\n        <button class=\"btn btn-primary btn-block\" id=\"quiz-btn\" type=\"button\">See my risk score<\/button>\n        <div class=\"readout\" id=\"quiz-out\" hidden><\/div>\n      <\/div>`;\n\n    \/\/ ---- mode + option state ----\n    let mode='random';\n    const opts={upper:true,lower:true,num:true,sym:true,space:false,avoid:false};\n    el.querySelectorAll('#pw-random-fields .tag-btn').forEach(b=>b.onclick=()=>{ b.classList.toggle('active'); opts[b.dataset.k]=b.classList.contains('active'); });\n    $('pw-avoid').onchange=e=>opts.avoid=e.target.checked;\n    $('pw-len').oninput=e=>$('pw-len-val').textContent=e.target.value;\n    $('pw-words').oninput=e=>$('pw-words-val').textContent=e.target.value;\n\n    $('pw-mode-seg').querySelectorAll('button').forEach(b=>b.onclick=()=>{\n      $('pw-mode-seg').querySelectorAll('button').forEach(x=>x.classList.remove('active')); b.classList.add('active');\n      mode=b.dataset.v;\n      $('pw-random-fields').hidden = !['random','wifi','business'].includes(mode);\n      $('pw-passphrase-fields').hidden = mode!=='passphrase';\n      $('pw-pin-fields').hidden = mode!=='pin';\n      if(mode==='wifi'){ $('pw-len').value=20; $('pw-len-val').textContent=20; opts.space=false; }\n      if(mode==='business'){ $('pw-len').value=14; $('pw-len-val').textContent=14; opts.upper=opts.lower=opts.num=opts.sym=true; el.querySelectorAll('#pw-random-fields .tag-btn').forEach(b=>b.classList.add('active')); }\n    });\n\n    \/\/ ---- quiz ----\n    const quizAns={reuse:null,length:null,tfa:null};\n    el.querySelectorAll('.quiz-opts').forEach(group=>{\n      group.querySelectorAll('.quiz-opt').forEach(b=>b.onclick=()=>{\n        group.querySelectorAll('.quiz-opt').forEach(x=>x.classList.remove('active'));\n        b.classList.add('active'); quizAns[group.dataset.q]=+b.dataset.v;\n      });\n    });\n    $('quiz-btn').onclick=()=>{\n      if(quizAns.reuse===null||quizAns.length===null||quizAns.tfa===null){ readoutErr('quiz-out','Please answer all three questions.'); return; }\n      const score=quizAns.reuse+quizAns.length+quizAns.tfa;\n      let msg, label;\n      if(score<=1){ label='Low risk \u2014 great habits!'; msg='You are following password best practices. Keep using a password manager and unique passwords everywhere.'; }\n      else if(score<=3){ label='Moderate risk'; msg='A few habits could be improved \u2014 consider enabling 2FA everywhere and lengthening shorter passwords.'; }\n      else { label='High risk'; msg='Your current habits leave you vulnerable to credential-stuffing attacks. Start using a password manager with unique, generated passwords and turn on 2FA.'; }\n      readout('quiz-out', label, msg);\n    };\n\n    \/\/ ---- generate ----\n    $('pw-btn').onclick=()=>{\n      let pass=null, entropyBits=0, isStructured=false;\n      if(mode==='random'||mode==='wifi'||mode==='business'){\n        const len=+$('pw-len').value;\n        let tries=0;\n        do{ pass=genRandom(len,opts); tries++; } while(pass && history.includes(pass) && tries<5);\n        if(!pass){ readoutErr('pw-strength-wrap','Select at least one character set.'); return; }\n        entropyBits = len*Math.log2(Math.max(2,poolSizeFor(opts)));\n      } else if(mode==='pronounceable'){\n        pass = genPronounceable(10);\n        isStructured=true;\n        entropyBits = pass.length*2.0;\n      } else if(mode==='passphrase'){\n        const wc=+$('pw-words').value, sep=$('pw-sep').value, cap=$('pw-cap').checked, addNum=$('pw-num2').checked;\n        pass = genPassphrase(wc,sep,cap,addNum);\n        isStructured=true;\n        entropyBits = wc*Math.log2(PASSPHRASE_WORDS.length) + (addNum?Math.log2(90):0);\n      } else if(mode==='pin'){\n        const len=+$('pw-pin-len').value;\n        pass = genPIN(len);\n        isStructured=true;\n        entropyBits = len*Math.log2(10);\n      }\n\n      history.push(pass); if(history.length>5) history.shift();\n\n      $('pw-terminal-box').hidden=false;\n      $('pw-value').textContent = pass;\n      $('pw-strength-wrap').hidden=false;\n\n      const str = strengthOf(entropyBits);\n      $('pw-strength-text').textContent = str.label;\n      $('pw-strength-pct').textContent = Math.round(entropyBits)+' bits of entropy';\n      $('pw-strength-fill').style.width = str.pct+'%';\n      $('pw-strength-fill').style.background = str.color;\n\n      const guessesPerSec = 1e10;\n      const combinations = Math.pow(2, entropyBits);\n      const crackSeconds = (combinations\/2)\/guessesPerSec;\n      $('pw-meta').innerHTML = [\n        ['Entropy',Math.round(entropyBits)+' bits'],\n        ['Est. crack time (offline attack)',formatCrackTime(crackSeconds)],\n        ['Length',pass.length+' chars']\n      ].map(([l,v])=>`<div class=\"m-item\"><span>${l}<\/span><b>${v}<\/b><\/div>`).join('');\n\n      let advice=[];\n      if(WEAK_PASSWORDS.includes(pass.toLowerCase())) advice.push('This exact password is a known extremely common password \u2014 never use it.');\n      if(!isStructured){\n        if(pass.length<12) advice.push('Consider increasing length to at least 12 characters \u2014 each extra character multiplies crack time.');\n        const typesUsed=[opts.upper,opts.lower,opts.num,opts.sym].filter(Boolean).length;\n        if(typesUsed<3) advice.push('Mixing more character types (uppercase, numbers, symbols) increases strength significantly.');\n        const strongerLen = Math.ceil(60\/Math.log2(Math.max(2,poolSizeFor(opts))));\n        if(entropyBits<60) advice.push(`Increasing length to ${strongerLen} characters would raise this to a \"Strong\" rating.`);\n      }\n      $('pw-advice').innerHTML = advice.length ? advice.map(a=>'\u26a0 '+a).join('<br>') : '\u2705 This password meets strong security recommendations.';\n\n      const counts={upper:0,lower:0,num:0,sym:0};\n      for(const ch of pass){\n        if(\/[A-Z]\/.test(ch)) counts.upper++;\n        else if(\/[a-z]\/.test(ch)) counts.lower++;\n        else if(\/[0-9]\/.test(ch)) counts.num++;\n        else counts.sym++;\n      }\n      const total=pass.length||1;\n      const distColors={upper:'#4C6FFF',lower:'#1E9E6B',num:'#F5A623',sym:'#C0392B'};\n      const labelMap={upper:'Uppercase',lower:'Lowercase',num:'Numbers',sym:'Symbols\/other'};\n      $('pw-dist').innerHTML = '<h5 style=\"font-size:.85rem;margin-bottom:10px;\">Character distribution<\/h5>' + Object.entries(counts).map(([k,v])=>{\n        const pct=Math.round(v\/total*100);\n        return `<div class=\"dist-row\"><span class=\"dist-label\">${labelMap[k]}<\/span><div class=\"dist-track\"><div class=\"dist-fill\" style=\"width:${pct}%;background:${distColors[k]};\"><\/div><\/div><span class=\"dist-pct\">${pct}%<\/span><\/div>`;\n      }).join('');\n\n      $('pw-breach-result').innerHTML='';\n    };\n\n    $('pw-copy').onclick=()=>{\n      const val=$('pw-value').textContent;\n      const done=()=>{ $('pw-copy').textContent='Copied!'; setTimeout(()=>$('pw-copy').textContent='Copy',1500); };\n      if(navigator.clipboard) navigator.clipboard.writeText(val).then(done).catch(done); else done();\n    };\n    $('pw-download').onclick=()=>{\n      const val=$('pw-value').textContent;\n      const blob=new Blob([val],{type:'text\/plain'});\n      const url=URL.createObjectURL(blob);\n      const a=document.createElement('a'); a.href=url; a.download='password.txt'; a.click();\n      URL.revokeObjectURL(url);\n    };\n    $('pw-breach').onclick=async()=>{\n      const val=$('pw-value').textContent;\n      $('pw-breach-result').textContent='Checking\u2026';\n      try{\n        const enc=new TextEncoder().encode(val);\n        const hashBuf=await crypto.subtle.digest('SHA-1',enc);\n        const hashArr=Array.from(new Uint8Array(hashBuf));\n        const hashHex=hashArr.map(b=>b.toString(16).padStart(2,'0')).join('').toUpperCase();\n        const prefix=hashHex.slice(0,5), suffix=hashHex.slice(5);\n        const res=await fetch('https:\/\/api.pwnedpasswords.com\/range\/'+prefix);\n        if(!res.ok) throw new Error('network');\n        const text=await res.text();\n        const match=text.split('\\n').find(l=>l.split(':')[0]===suffix);\n        const count=match?parseInt(match.split(':')[1]):0;\n        $('pw-breach-result').innerHTML = count>0\n          ? `\u26a0 This exact password has appeared in ${count.toLocaleString()} known data breaches \u2014 avoid using it.`\n          : '\u2705 Not found in the breach database checked (uses the k-anonymity model \u2014 only a partial hash prefix is ever sent).';\n      }catch(e){\n        $('pw-breach-result').textContent = 'Could not reach the breach-check service right now \u2014 this needs an internet connection.';\n      }\n    };\n  }});\n\nreg({slug:'unit-conversion',name:'Unit Conversion Calculator',cat:'everyday',icn:'ruler',photo:42,\n  desc:'A comprehensive measurement hub covering 16 categories, with live conversion, step-by-step formulas, and instant multi-unit results.',\n  render(el){\n    const CATEGORIES={\n      length:{label:'Length',units:{m:{label:'Meters',f:1},km:{label:'Kilometers',f:1000},cm:{label:'Centimeters',f:0.01},mm:{label:'Millimeters',f:0.001},mi:{label:'Miles',f:1609.344},yd:{label:'Yards',f:0.9144},ft:{label:'Feet',f:0.3048},in:{label:'Inches',f:0.0254},nmi:{label:'Nautical miles',f:1852}}},\n      weight:{label:'Weight & Mass',units:{kg:{label:'Kilograms',f:1},g:{label:'Grams',f:0.001},mg:{label:'Milligrams',f:1e-6},tonne:{label:'Metric tons',f:1000},lb:{label:'Pounds',f:0.45359237},oz:{label:'Ounces',f:0.0283495231},stone:{label:'Stone',f:6.35029318}}},\n      area:{label:'Area',units:{sqm:{label:'Square meters',f:1},sqkm:{label:'Square kilometers',f:1e6},hectare:{label:'Hectares',f:10000},sqft:{label:'Square feet',f:0.09290304},sqyd:{label:'Square yards',f:0.83612736},acre:{label:'Acres',f:4046.8564224},sqmi:{label:'Square miles',f:2589988.110336},sqin:{label:'Square inches',f:0.00064516}}},\n      volume:{label:'Volume',units:{l:{label:'Liters',f:1},ml:{label:'Milliliters',f:0.001},m3:{label:'Cubic meters',f:1000},gal_us:{label:'US Gallons',f:3.785411784},gal_uk:{label:'UK Gallons',f:4.54609},qt_us:{label:'US Quarts',f:0.946352946},pt_us:{label:'US Pints',f:0.473176473},cup_us:{label:'US Cups',f:0.2365882365},floz_us:{label:'US Fluid Ounces',f:0.0295735296},tbsp:{label:'Tablespoons',f:0.0147867648},tsp:{label:'Teaspoons',f:0.00492892159},ft3:{label:'Cubic feet',f:28.316846592},in3:{label:'Cubic inches',f:0.016387064}}},\n      time:{label:'Time',units:{s:{label:'Seconds',f:1},min:{label:'Minutes',f:60},hr:{label:'Hours',f:3600},day:{label:'Days',f:86400},week:{label:'Weeks',f:604800},month:{label:'Months (avg)',f:2629800},year:{label:'Years',f:31557600}}},\n      speed:{label:'Speed',units:{mps:{label:'Meters\/second',f:1},kmh:{label:'Kilometers\/hour',f:0.277778},mph:{label:'Miles\/hour',f:0.44704},knot:{label:'Knots',f:0.514444},fps:{label:'Feet\/second',f:0.3048}}},\n      pressure:{label:'Pressure',units:{pa:{label:'Pascals',f:1},kpa:{label:'Kilopascals',f:1000},bar:{label:'Bar',f:100000},atm:{label:'Atmospheres',f:101325},psi:{label:'PSI',f:6894.76},torr:{label:'Torr',f:133.322}}},\n      energy:{label:'Energy',units:{j:{label:'Joules',f:1},kj:{label:'Kilojoules',f:1000},cal:{label:'Calories',f:4.184},kcal:{label:'Kilocalories',f:4184},wh:{label:'Watt-hours',f:3600},kwh:{label:'Kilowatt-hours',f:3.6e6},btu:{label:'BTU',f:1055.06}}},\n      power:{label:'Power',units:{w:{label:'Watts',f:1},kw:{label:'Kilowatts',f:1000},mw:{label:'Megawatts',f:1e6},hp:{label:'Horsepower',f:745.7}}},\n      force:{label:'Force',units:{n:{label:'Newtons',f:1},kn:{label:'Kilonewtons',f:1000},lbf:{label:'Pound-force',f:4.44822},kgf:{label:'Kilogram-force',f:9.80665},dyne:{label:'Dyne',f:1e-5}}},\n      density:{label:'Density',units:{kgm3:{label:'kg\/m\u00b3',f:1},gcm3:{label:'g\/cm\u00b3',f:1000},lbft3:{label:'lb\/ft\u00b3',f:16.0185},lbin3:{label:'lb\/in\u00b3',f:27679.9}}},\n      angle:{label:'Angle',units:{deg:{label:'Degrees',f:1},rad:{label:'Radians',f:57.29577951},grad:{label:'Gradians',f:0.9},arcmin:{label:'Arcminutes',f:0.0166667},arcsec:{label:'Arcseconds',f:0.000277778}}},\n      frequency:{label:'Frequency',units:{hz:{label:'Hertz',f:1},khz:{label:'Kilohertz',f:1000},mhz:{label:'Megahertz',f:1e6},ghz:{label:'Gigahertz',f:1e9},rpm:{label:'RPM',f:0.0166667}}},\n      data:{label:'Data Storage',units:{byte:{label:'Bytes',f:1},bit:{label:'Bits',f:0.125},kb:{label:'KB (decimal)',f:1000},mb:{label:'MB (decimal)',f:1e6},gb:{label:'GB (decimal)',f:1e9},tb:{label:'TB (decimal)',f:1e12},kib:{label:'KiB (binary)',f:1024},mib:{label:'MiB (binary)',f:1048576},gib:{label:'GiB (binary)',f:1073741824},tib:{label:'TiB (binary)',f:1099511627776}}},\n      temperature:{label:'Temperature',special:true,units:{C:{label:'Celsius'},F:{label:'Fahrenheit'},K:{label:'Kelvin'}}},\n      fuelEconomy:{label:'Fuel Economy',special:true,units:{mpg_us:{label:'MPG (US)'},mpg_uk:{label:'MPG (UK)'},l100km:{label:'L\/100km'},kml:{label:'km\/L'}}}\n    };\n    const POPULAR=[\n      ['Inches \u2192 cm','length','in','cm'],['cm \u2192 Inches','length','cm','in'],['kg \u2192 lbs','weight','kg','lb'],['lbs \u2192 kg','weight','lb','kg'],\n      ['Celsius \u2192 Fahrenheit','temperature','C','F'],['km \u2192 Miles','length','km','mi'],['Liters \u2192 Gallons','volume','l','gal_us'],['MB \u2192 GB','data','mb','gb']\n    ];\n    let history=[];\n\n    function toL100km(unit,v){\n      if(unit==='mpg_us') return 235.214583\/v;\n      if(unit==='mpg_uk') return 282.480936\/v;\n      if(unit==='kml') return 100\/v;\n      return v;\n    }\n    function fromL100km(unit,l){\n      if(unit==='mpg_us') return 235.214583\/l;\n      if(unit==='mpg_uk') return 282.480936\/l;\n      if(unit==='kml') return 100\/l;\n      return l;\n    }\n    function convert(cat,from,to,val){\n      if(cat==='temperature'){\n        let c = from==='C'?val: from==='F'?(val-32)*5\/9 : val-273.15;\n        return to==='C'?c: to==='F'?c*9\/5+32 : c+273.15;\n      }\n      if(cat==='fuelEconomy'){\n        const l=toL100km(from,val);\n        return fromL100km(to,l);\n      }\n      const u=CATEGORIES[cat].units;\n      return (val*u[from].f)\/u[to].f;\n    }\n    function insightFor(cat,unit,val){\n      let meters=null, kg=null, liters=null;\n      if(cat==='length') meters = val*CATEGORIES.length.units[unit].f;\n      if(cat==='weight') kg = val*CATEGORIES.weight.units[unit].f;\n      if(cat==='volume') liters = val*CATEGORIES.volume.units[unit].f;\n      if(meters!==null){\n        if(meters>=90&&meters<=110) return 'That is about the length of a football\/soccer field (100 m).';\n        if(meters>=1.6&&meters<=2.0) return 'That is roughly the height of an average adult.';\n        if(meters>=0.28&&meters<=0.32) return 'That is about the length of a standard school ruler (30 cm).';\n        if(meters>=1500&&meters<=1700) return 'That is roughly the distance of one lap around a typical athletics track times four (~1.6 km).';\n      }\n      if(kg!==null){\n        if(kg>=70&&kg<=90) return 'That is roughly the weight of an average adult male.';\n        if(kg>=150&&kg<=250) return 'That is in the range of an adult lion weight.';\n        if(kg>=900&&kg<=1100) return 'That is about the weight of a small car (roughly 1 tonne).';\n      }\n      if(liters!==null){\n        if(liters>=0.4&&liters<=0.6) return 'That is about the volume of a typical water bottle.';\n        if(liters>=3.5&&liters<=4) return 'That is roughly one US gallon of liquid.';\n      }\n      return null;\n    }\n\n    el.innerHTML = `\n      <div class=\"uc-shell\">\n      <div class=\"uc-chip-row\" id=\"uc-popular\"><\/div>\n      <div class=\"field\"><label>Category<\/label>\n        <select id=\"uc-cat\">${Object.entries(CATEGORIES).map(([k,v])=>`<option value=\"${k}\" ${k==='length'?'selected':''}>${v.label}<\/option>`).join('')}<\/select>\n      <\/div>\n      <div class=\"uc-from-to-row\">\n        <div class=\"field\"><label>From<\/label><select id=\"uc-from\"><\/select><\/div>\n        <button class=\"uc-swap-btn\" id=\"uc-swap\" type=\"button\" title=\"Swap units\">\u21c4<\/button>\n        <div class=\"field\"><label>To<\/label><select id=\"uc-to\"><\/select><\/div>\n      <\/div>\n      <div class=\"field\"><label>Value<\/label><input type=\"number\" id=\"uc-val\" value=\"15\"><\/div>\n\n      <div class=\"dd-main-result\" id=\"uc-main-result\"><\/div>\n      <div id=\"uc-formula\" style=\"margin-top:14px;\"><\/div>\n      <div id=\"uc-insight\"><\/div>\n\n      <div class=\"calc-section section-blue\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('ruler',16)}<\/div><h5>All units at once<\/h5><\/div>\n        <div class=\"uc-results-grid\" id=\"uc-multi\"><\/div>\n      <\/div>\n\n      <div class=\"calc-section section-green\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('chart',16)}<\/div><h5>Conversion table<\/h5><\/div>\n        <div id=\"uc-table\"><\/div>\n      <\/div>\n\n      <div class=\"calc-section section-amber\" id=\"uc-special\" hidden><\/div>\n\n      <div class=\"calc-section section-navy\" id=\"uc-recent-section\" hidden>\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('clock',16)}<\/div><h5>Recent conversions (this session)<\/h5><\/div>\n        <div id=\"uc-recent\"><\/div>\n      <\/div>\n\n      <div class=\"calc-section section-blue\">\n        <div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('search',16)}<\/div><h5>Export &amp; share<\/h5><\/div>\n        <div class=\"form-row\">\n          <button class=\"btn btn-ghost btn-block\" id=\"uc-copy\" type=\"button\">Copy result<\/button>\n          <button class=\"btn btn-ghost btn-block\" id=\"uc-print\" type=\"button\">Print<\/button>\n        <\/div>\n      <\/div>\n      <p style=\"font-size:.75rem;color:var(--text-soft);margin-top:14px;\">\ud83d\udd12 All conversions are calculated locally in your browser \u2014 nothing is sent anywhere.<\/p>\n      <\/div>`;\n\n    $('uc-popular').innerHTML = POPULAR.map((p,i)=>`<button class=\"tag-btn\" data-i=\"${i}\">${p[0]}<\/button>`).join('');\n    $('uc-popular').querySelectorAll('[data-i]').forEach(b=>b.onclick=()=>{\n      const [,cat,from,to]=POPULAR[+b.dataset.i];\n      $('uc-cat').value=cat; fillUnits(); $('uc-from').value=from; $('uc-to').value=to; runConvert();\n    });\n\n    function fillUnits(){\n      const cat=$('uc-cat').value;\n      const keys=Object.keys(CATEGORIES[cat].units);\n      $('uc-from').innerHTML=keys.map(k=>`<option value=\"${k}\">${CATEGORIES[cat].units[k].label}<\/option>`).join('');\n      $('uc-to').innerHTML=keys.map((k,i)=>`<option value=\"${k}\" ${i===1?'selected':''}>${CATEGORIES[cat].units[k].label}<\/option>`).join('');\n    }\n    fillUnits();\n    $('uc-cat').onchange=()=>{ fillUnits(); runConvert(); };\n    $('uc-from').onchange=runConvert; $('uc-to').onchange=runConvert; $('uc-val').oninput=runConvert;\n    $('uc-swap').onclick=()=>{ const f=$('uc-from').value; $('uc-from').value=$('uc-to').value; $('uc-to').value=f; runConvert(); };\n\n    function runConvert(){\n      const cat=$('uc-cat').value, from=$('uc-from').value, to=$('uc-to').value, val=+$('uc-val').value||0;\n      const cfg=CATEGORIES[cat];\n      const result=convert(cat,from,to,val);\n\n      $('uc-main-result').innerHTML = `<div class=\"dd-main-value\">${num(result,6)} ${cfg.units[to].label}<\/div><div class=\"dd-main-sub\">${val} ${cfg.units[from].label} converted<\/div>`;\n\n      \/\/ formula \/ step-by-step\n      let formulaHTML;\n      if(cat==='temperature'){\n        const steps={\n          'C-F':'\u00b0F = \u00b0C \u00d7 9\/5 + 32','F-C':'\u00b0C = (\u00b0F \u2212 32) \u00d7 5\/9','C-K':'K = \u00b0C + 273.15','K-C':'\u00b0C = K \u2212 273.15','F-K':'K = (\u00b0F \u2212 32) \u00d7 5\/9 + 273.15','K-F':'\u00b0F = (K \u2212 273.15) \u00d7 9\/5 + 32','C-C':'No conversion needed','F-F':'No conversion needed','K-K':'No conversion needed'\n        };\n        formulaHTML = `<div class=\"formula-box\"><div class=\"f-label\">Formula used<\/div><div class=\"f-eq\">${steps[from+'-'+to]||''}<\/div><\/div>`;\n      } else if(cat==='fuelEconomy'){\n        formulaHTML = `<div class=\"formula-box\"><div class=\"f-label\">Formula used<\/div><div class=\"f-eq\">Converted via L\/100km as a common intermediate (fuel economy is a reciprocal relationship, not a simple multiplier)<\/div><\/div>`;\n      } else {\n        const ff=cfg.units[from].f, ft=cfg.units[to].f;\n        formulaHTML = `<div class=\"formula-box\"><div class=\"f-label\">Step-by-step<\/div><div class=\"f-eq\">${val} \u00d7 (${num(ff,8)} \u00f7 ${num(ft,8)}) = ${val} \u00d7 ${num(ff\/ft,8)} = ${num(result,6)}<\/div><\/div>`;\n      }\n      $('uc-formula').innerHTML = formulaHTML;\n\n      const insight=insightFor(cat,from,val);\n      $('uc-insight').innerHTML = insight ? `<div class=\"example-box\"><div class=\"e-label\">For context<\/div><p style=\"margin:0;\">${insight}<\/p><\/div>` : '';\n\n      \/\/ multi-unit grid\n      const keys=Object.keys(cfg.units);\n      $('uc-multi').innerHTML = keys.map(k=>{\n        const v=convert(cat,from,k,val);\n        return `<div class=\"uc-result-card\"><div class=\"uc-r-unit\">${cfg.units[k].label}<\/div><div class=\"uc-r-val\">${num(v,4)}<\/div><\/div>`;\n      }).join('');\n\n      \/\/ conversion table\n      const refVals=[1,2,5,10,20,50,100];\n      $('uc-table').innerHTML = `<table class=\"ref-table\"><thead><tr><th>${cfg.units[from].label}<\/th><th>${cfg.units[to].label}<\/th><\/tr><\/thead><tbody>${refVals.map(v=>`<tr><td>${v}<\/td><td>${num(convert(cat,from,to,v),4)}<\/td><\/tr>`).join('')}<\/tbody><\/table>`;\n\n      \/\/ special notes\n      if(cat==='data'){\n        $('uc-special').hidden=false;\n        $('uc-special').innerHTML=`<div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('lock',16)}<\/div><h5>Binary vs. decimal storage<\/h5><\/div><p style=\"color:var(--text-soft);font-size:.88rem;\">Storage manufacturers typically use decimal units (1 KB = 1,000 bytes), while operating systems often report binary units (1 KiB = 1,024 bytes). This is why a \"500 GB\" drive often shows as roughly 465 GB in your computer's file manager \u2014 both figures are correct, just using different definitions.<\/p>`;\n      } else if(cat==='fuelEconomy'){\n        $('uc-special').hidden=false;\n        $('uc-special').innerHTML=`<div class=\"calc-section-head\"><div class=\"cs-icn\">${icon('car',16)}<\/div><h5>About fuel economy units<\/h5><\/div><p style=\"color:var(--text-soft);font-size:.88rem;\">US and UK gallons are different sizes (a UK\/imperial gallon is about 20% larger), so MPG figures aren't directly comparable between the two without conversion. L\/100km is the inverse relationship to MPG \u2014 lower is better in L\/100km, while higher is better in MPG.<\/p>`;\n      } else {\n        $('uc-special').hidden=true;\n      }\n\n      \/\/ history\n      history.unshift({cat:cfg.label,from:cfg.units[from].label,to:cfg.units[to].label,val,result});\n      if(history.length>8) history.pop();\n      $('uc-recent-section').hidden=false;\n      $('uc-recent').innerHTML = history.map(h=>`<div class=\"uc-recent-item\"><span>${h.val} ${h.from} \u2192 ${h.to}<\/span><span>${num(h.result,4)}<\/span><\/div>`).join('');\n    }\n    runConvert();\n\n    $('uc-copy').onclick=()=>{\n      const val=$('uc-val').value, from=CATEGORIES[$('uc-cat').value].units[$('uc-from').value].label, to=CATEGORIES[$('uc-cat').value].units[$('uc-to').value].label;\n      const text=`${val} ${from} = ${$('uc-main-result').querySelector('.dd-main-value').textContent}`;\n      const done=()=>{$('uc-copy').textContent='Copied!';setTimeout(()=>$('uc-copy').textContent='Copy result',1500);};\n      navigator.clipboard?navigator.clipboard.writeText(text).then(done).catch(done):done();\n    };\n    $('uc-print').onclick=()=>window.print();\n  }});\n\n\/* ===================================================================\n   CALC_INFO \u2014 detailed \"how it works\" explainer content per calculator\n   (overview, formula box, worked example, reference tables\/FAQs)\n=================================================================== *\/\nconst CALC_INFO = {\n\nmortgage:`\n<h2>How the Mortgage Calculator works<\/h2>\n<p>This calculator estimates your full monthly housing payment \u2014 not just principal and interest, but property tax, homeowners insurance, PMI and HOA fees too \u2014 and builds a complete amortization schedule showing exactly how your balance declines every year (or every month) until payoff.<\/p>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Core payment formula<\/div>\n  <div class=\"f-eq\">M = P \u00d7 [ r(1+r)\u207f ] \/ [ (1+r)\u207f \u2212 1 ]<\/div>\n<\/div>\n<p>P is the loan principal (home price minus down payment), r is the monthly interest rate (annual rate divided by 12), and n is the total number of monthly payments (loan term in years multiplied by 12). This produces a level payment that fully retires the loan by the final month.<\/p>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">A $400,000 home with 20% down leaves a $320,000 loan. At 6.538% over 30 years, that works out to a principal-and-interest payment of exactly <b>$2,030.62<\/b> a month \u2014 before taxes, insurance or fees are added on top.<\/p>\n<\/div>\n\n<h2>What actually makes up your monthly payment<\/h2>\n<p>Lenders often refer to a mortgage payment as \"PITI\" \u2014 Principal, Interest, Taxes and Insurance. This calculator adds two more common line items, PMI and HOA fees, to reflect the full out-of-pocket cost most homeowners actually pay each month.<\/p>\n<table class=\"ref-table\">\n  <thead><tr><th>Component<\/th><th>What it is<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Principal &amp; interest<\/td><td>Repays the loan itself, split between reducing the balance and compensating the lender<\/td><\/tr>\n    <tr><td>Property tax<\/td><td>Charged by your local government, often billed annually but collected monthly by your lender<\/td><\/tr>\n    <tr><td>Home insurance<\/td><td>Protects the home against fire, storm and other covered damage \u2014 usually required by the lender<\/td><\/tr>\n    <tr><td>PMI<\/td><td>Private mortgage insurance, required on most conventional loans when your down payment is below 20%<\/td><\/tr>\n    <tr><td>HOA fee<\/td><td>Charged by a homeowners association for shared amenities and maintenance, where applicable<\/td><\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>Fixed-rate vs. adjustable-rate mortgages<\/h2>\n<p>A fixed-rate mortgage keeps the same interest rate for the entire term, so your principal-and-interest payment never changes \u2014 this calculator models a fixed-rate loan. An adjustable-rate mortgage (ARM) starts with a lower introductory rate that later adjusts periodically based on a market index, which can raise or lower your payment over time. Fixed-rate loans offer predictability; ARMs can offer short-term savings but carry more long-term uncertainty.<\/p>\n\n<h2>Understanding PMI and how to avoid it<\/h2>\n<p>Private mortgage insurance protects the lender, not the borrower, and typically applies whenever a conventional loan's down payment is under 20%. It's usually cancellable once your loan balance drops to about 80% of the home's original value \u2014 this calculator automatically stops counting PMI in the schedule once that threshold is crossed. Putting down a full 20% upfront, or refinancing once you've built enough equity, are the two most common ways to eliminate it.<\/p>\n\n<h2>How amortization works<\/h2>\n<p>Every fixed-rate payment is split between interest and principal, but that split shifts dramatically over the life of the loan. Early payments are weighted heavily toward interest because the balance is still large; later payments apply far more to principal as the balance shrinks. The annual and monthly schedules above show this shift precisely, year by year or month by month, along with the running ending balance.<\/p>\n\n<h2>Tips for a lower monthly payment<\/h2>\n<details><summary>Does a bigger down payment always help?<\/summary><p>Yes \u2014 a larger down payment reduces the loan amount directly, lowers your monthly principal and interest, and can eliminate PMI entirely if it reaches 20% of the purchase price.<\/p><\/details>\n<details><summary>Should I choose a 15-year or 30-year term?<\/summary><p>A 15-year term carries a higher monthly payment but a lower interest rate and dramatically less total interest paid. A 30-year term lowers the monthly payment, offering more flexibility, at the cost of more interest over time.<\/p><\/details>\n<details><summary>Does making extra payments actually help?<\/summary><p>Yes \u2014 any extra amount applied straight to principal each month shortens the loan and reduces total interest, since every dollar of extra principal stops accruing interest immediately. Use the \"More options\" field above to model this.<\/p><\/details>\n<details><summary>What is a mortgage payoff date?<\/summary><p>It's the month your final scheduled payment is due, assuming you pay exactly the calculated amount every month with no missed or additional payments.<\/p><\/details>`,\n\nloan:`\n<h2>Loan calculator \u2014 payments, amortization, and comparison in one place<\/h2>\n<p>This calculator does more than compute a monthly payment. It handles monthly, bi-weekly, or weekly payment frequencies, down payments and origination fees, extra and one-time payments, a full amortization schedule with CSV export, an extra-payment savings simulator, and a side-by-side comparison against an alternate loan offer.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Standard amortization formula<\/div>\n  <div class=\"f-eq\">Payment = P \u00d7 [ r(1+r)\u207f ] \u00f7 [ (1+r)\u207f \u2212 1 ]<\/div>\n<\/div>\n<p>P is the loan principal, r is the interest rate per payment period, and n is the total number of payments. This same formula \u2014 used by virtually every lender \u2014 produces a fixed payment that includes both interest and principal, structured so the balance reaches exactly zero at the end of the term.<\/p>\n\n<h2>What is a loan?<\/h2>\n<p>A loan is money borrowed that's repaid over time with interest \u2014 the cost the lender charges for the use of their money. Nearly all consumer loans (personal, auto, student, and most mortgages) are \"amortizing,\" meaning each payment is the same fixed amount, but the mix between interest and principal shifts over the life of the loan.<\/p>\n\n<h2>Types of loans<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Type<\/th><th>Typical use<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Personal loan<\/td><td>Debt consolidation, unexpected expenses, general purchases<\/td><\/tr>\n    <tr><td>Auto loan<\/td><td>Vehicle purchase, usually secured by the car itself<\/td><\/tr>\n    <tr><td>Mortgage \/ home loan<\/td><td>Property purchase, typically the largest and longest-term consumer loan<\/td><\/tr>\n    <tr><td>Student \/ education loan<\/td><td>Tuition and education costs, often with deferred payment options<\/td><\/tr>\n    <tr><td>Business loan<\/td><td>Working capital, equipment, or expansion for a business<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>The math behind this calculator applies to any of them \u2014 what changes between loan types is typically the rate, term, and whether the loan is secured (backed by collateral) or unsecured.<\/p>\n\n<h2>How loan payments work \u2014 understanding amortization<\/h2>\n<p>Early in a loan's life, most of each payment goes toward interest, since interest is calculated on the (still-large) remaining balance. As the balance shrinks, more of each fixed payment shifts toward principal \u2014 this gradual shift is what \"amortization\" describes, and it's why the interest-paid-per-year chart above shows a declining trend even though the payment itself never changes.<\/p>\n\n<h2>Fixed vs. variable interest rates<\/h2>\n<p>A <b>fixed rate<\/b> stays the same for the entire loan term, making payments completely predictable \u2014 this is what this calculator assumes. A <b>variable rate<\/b> can rise or fall over time based on a benchmark index, which means payments can change, sometimes significantly. Fixed rates offer certainty; variable rates sometimes start lower but carry the risk of increasing later.<\/p>\n\n<h2>APR vs. interest rate<\/h2>\n<p>The interest rate reflects only the cost of borrowing the principal. <b>APR<\/b> (Annual Percentage Rate) folds in certain additional fees (like origination fees) to represent the loan's total yearly cost as a single percentage \u2014 which is why comparing APRs, not just interest rates, gives a fairer comparison between loan offers that have different fee structures.<\/p>\n\n<h2>Factors that affect your loan cost<\/h2>\n<ul>\n<li><b>Interest rate<\/b> \u2014 the single biggest lever; even small rate differences compound significantly over a multi-year term.<\/li>\n<li><b>Loan term<\/b> \u2014 a longer term lowers the monthly payment but increases total interest paid, since interest accrues for more periods.<\/li>\n<li><b>Payment frequency<\/b> \u2014 more frequent payments (bi-weekly, weekly) can modestly reduce total interest, since principal is reduced slightly more often.<\/li>\n<li><b>Fees<\/b> \u2014 origination and processing fees add to the effective cost of borrowing beyond the stated interest rate.<\/li>\n<li><b>Down payment<\/b> \u2014 reducing the amount financed directly reduces both the payment and total interest.<\/li>\n<\/ul>\n\n<h2>Is a longer loan term cheaper?<\/h2>\n<p>Not in total \u2014 a longer term almost always costs more in total interest, even though the monthly payment is lower. The trade-off is monthly affordability versus total cost: a shorter term costs less overall but requires a higher payment each period. Use the comparison section above to see this trade-off numerically for your own numbers.<\/p>\n\n<h2>Should I make extra payments?<\/h2>\n<p>Extra payments go directly toward principal (assuming no prepayment penalty), which reduces the balance interest is calculated on for every remaining period \u2014 meaning even modest extra payments can produce outsized interest savings over a long loan. The extra-payment simulator above shows the exact interest saved and time saved for any extra amount you enter, specific to your loan's numbers.<\/p>\n\n<h2>Tips to reduce interest<\/h2>\n<ul>\n<li>Make extra payments whenever possible, even small, irregular ones.<\/li>\n<li>Choose the shortest term you can comfortably afford.<\/li>\n<li>Shop multiple lenders and compare APR, not just the advertised rate.<\/li>\n<li>Consider bi-weekly payments if your lender allows them without extra fees.<\/li>\n<li>Avoid unnecessary add-on fees and optional insurance products bundled into the loan.<\/li>\n<\/ul>\n\n<h2>Common borrowing mistakes<\/h2>\n<p>Common mistakes include focusing only on the monthly payment without checking the total cost, not comparing APR across offers, ignoring prepayment penalty clauses before planning extra payments, and borrowing more than genuinely needed simply because it was approved.<\/p>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/mortgage\">Mortgage Calculator<\/a> for home loans, the <a href=\"#\/calculator\/auto-loan\">Auto Loan Calculator<\/a> for vehicle financing, the <a href=\"#\/calculator\/interest\">Interest Calculator<\/a> and <a href=\"#\/calculator\/compound-interest\">Compound Interest Calculator<\/a> for related interest math, and the <a href=\"#\/calculator\/amortization\">Amortization Calculator<\/a> for a dedicated schedule tool.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How is loan interest calculated?<\/summary><p>Interest for each period is the remaining balance multiplied by the periodic interest rate (annual rate divided by payments per year) \u2014 this calculator recalculates it fresh every period as the balance declines.<\/p><\/details>\n<details><summary>What affects my monthly payment?<\/summary><p>Primarily the loan amount, interest rate, and term \u2014 higher amounts and rates increase payments, while longer terms decrease them.<\/p><\/details>\n<details><summary>Is a longer loan term cheaper?<\/summary><p>Not in total interest \u2014 longer terms lower the monthly payment but almost always increase the total interest paid over the life of the loan.<\/p><\/details>\n<details><summary>Should I make extra payments?<\/summary><p>Generally yes if there's no prepayment penalty \u2014 extra payments reduce principal directly, cutting the interest calculated on all future periods.<\/p><\/details>\n<details><summary>Fixed vs. variable interest \u2014 which is better?<\/summary><p>Fixed rates offer payment certainty for the full term; variable rates may start lower but can rise, so the \"better\" choice depends on your risk tolerance and rate outlook.<\/p><\/details>\n<details><summary>What is APR?<\/summary><p>Annual Percentage Rate \u2014 the interest rate plus certain fees, expressed as one yearly percentage, giving a more complete cost comparison between loan offers.<\/p><\/details>\n<details><summary>How does bi-weekly payment reduce interest?<\/summary><p>Paying more frequently reduces the balance slightly sooner each cycle, which modestly lowers the interest that accrues compared to standard monthly payments.<\/p><\/details>\n<details><summary>Can I repay my loan early?<\/summary><p>Often yes, though some loans include prepayment penalties \u2014 check your specific loan agreement before making large extra payments.<\/p><\/details>\n<details><summary>What happens if I miss a payment?<\/summary><p>Typically late fees and potential credit score impact, and interest continues accruing on the outstanding balance \u2014 specific consequences vary by lender and loan agreement.<\/p><\/details>\n<details><summary>Does refinancing always save money?<\/summary><p>No \u2014 it depends on the new rate, remaining term, and any refinancing fees; refinancing to a lower rate but a longer term can sometimes increase total interest despite lowering the payment.<\/p><\/details>\n<details><summary>What is the difference between a secured and unsecured loan?<\/summary><p>A secured loan is backed by collateral (like a car or house) that the lender can claim if you default; an unsecured loan isn't backed by collateral and often carries a higher interest rate as a result.<\/p><\/details>\n<details><summary>How does the down payment affect my loan?<\/summary><p>A larger down payment reduces the principal financed, which directly lowers both your payment and total interest paid.<\/p><\/details>\n<details><summary>What's the difference between principal and interest?<\/summary><p>Principal is the amount borrowed; interest is the cost charged for borrowing it \u2014 every payment splits between the two, with the split shifting over time.<\/p><\/details>\n<details><summary>Why does my early amortization schedule show mostly interest?<\/summary><p>Interest is calculated on the outstanding balance, which is largest early in the loan \u2014 so the interest portion of each payment naturally starts higher and declines as the balance shrinks.<\/p><\/details>\n<details><summary>Can I compare two loan offers with different terms?<\/summary><p>Yes \u2014 the comparison section above lets you enter an alternate rate and term to see payment, total interest, and total cost side by side against your primary loan.<\/p><\/details>\n<details><summary>What's an origination fee?<\/summary><p>A one-time fee some lenders charge to process a loan, usually a percentage of the loan amount, added to your effective borrowing cost.<\/p><\/details>\n<details><summary>Does the payment frequency change my total interest much?<\/summary><p>Usually only modestly \u2014 the interest rate and term have a much larger effect on total interest than switching between monthly, bi-weekly, and weekly payments.<\/p><\/details>\n<details><summary>Is this calculator accurate for real lenders?<\/summary><p>It uses the same standard amortization formula lenders use for fixed-rate loans \u2014 actual offers may include additional fees or rounding conventions specific to that lender, so treat results as a close, reliable estimate rather than a binding quote.<\/p><\/details>`,\n\n'auto-loan':`\n<h2>Auto loan calculator \u2014 the true cost of financing a car<\/h2>\n<p>This calculator goes beyond a monthly payment. It accounts for trade-in equity (including negative equity), sales tax and fees, shows a full amortization schedule, simulates down payment and extra-payment scenarios, estimates fuel costs and depreciation, and compares leasing against buying \u2014 giving you the complete financial picture before you sign anything.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Amount financed<\/div>\n  <div class=\"f-eq\">Amount financed = Price + Sales tax + Fees \u2212 Down payment \u2212 Trade-in equity<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Monthly payment<\/div>\n  <div class=\"f-eq\">Payment = P \u00d7 [ r(1+r)\u207f ] \u00f7 [ (1+r)\u207f \u2212 1 ]<\/div>\n<\/div>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">A $28,000 vehicle with a $3,000 down payment, 6% sales tax, and $450 in fees, financed at 6.9% over 60 months, comes to a monthly payment of about <b>$536<\/b>, with roughly $5,026 in total interest.<\/p>\n<\/div>\n\n<h2>What is an auto loan?<\/h2>\n<p>An auto loan is a secured loan used to purchase a vehicle, where the car itself serves as collateral \u2014 meaning the lender can repossess it if payments aren't made. Like most consumer loans, auto loans are amortizing: a fixed monthly payment that gradually shifts from mostly interest early on to mostly principal later.<\/p>\n\n<h2>How auto financing works<\/h2>\n<p>The amount you finance isn't just the vehicle price \u2014 it typically includes sales tax and various fees (registration, title, documentation, dealer fees), reduced by your down payment and any trade-in equity. Getting this \"amount financed\" figure right is the foundation for an accurate payment estimate, which is why this calculator breaks out each component separately.<\/p>\n\n<h2>Understanding APR vs. interest rate<\/h2>\n<p>The interest rate reflects the base cost of borrowing; <b>APR<\/b> (Annual Percentage Rate) can include certain fees folded into a single yearly percentage, giving a more complete picture of a loan's true cost. When comparing offers from different lenders, comparing APR \u2014 not just the advertised rate \u2014 gives a fairer comparison.<\/p>\n\n<h2>Trade-in equity \u2014 positive and negative<\/h2>\n<p>If your trade-in is worth more than what you still owe on it, you have <b>positive equity<\/b>, which reduces the amount you need to finance on the new vehicle. If you owe more than it's worth, you have <b>negative equity<\/b> \u2014 this shortfall gets rolled into your new loan, increasing the amount financed and, often, your total interest paid. This calculator handles both cases automatically based on your trade-in value and payoff amount.<\/p>\n\n<h2>New vs. used car financing<\/h2>\n<p>New cars typically qualify for lower interest rates than used cars, partly because lenders view them as lower risk (more predictable value, manufacturer warranties). Used cars usually cost less upfront and depreciate more slowly in percentage terms going forward, since the steepest depreciation typically happens in a vehicle's first few years.<\/p>\n\n<h2>Lease vs. buy<\/h2>\n<p>Leasing typically has a lower monthly payment and lets you drive a new vehicle every few years, but you build no ownership equity and face mileage limits and wear-and-tear charges. Buying costs more per month (or requires a larger amount financed) but you own an asset at the end, which retains some resale or trade-in value. The lease-vs-buy comparison above runs the numbers for your specific scenario \u2014 but remember that once a loan is paid off, buying also eliminates the payment entirely, an ongoing advantage leasing never provides since a new lease payment begins again immediately.<\/p>\n\n<h2>Total cost of ownership \u2014 beyond the payment<\/h2>\n<p>The loan payment is only part of what a vehicle actually costs. Insurance, fuel, maintenance, and repairs add substantially to the real monthly cost of owning a car \u2014 the \"true monthly cost\" section above combines your loan payment with insurance and maintenance estimates for a more honest total, and the fuel cost estimator adds the largest remaining piece.<\/p>\n\n<h2>Vehicle depreciation<\/h2>\n<p>Cars are depreciating assets \u2014 a commonly cited approximation is roughly 20% value loss in the first year, followed by around 15% per year afterward, though actual depreciation varies significantly by make, model, mileage, and condition. Understanding depreciation matters for trade-in planning and for evaluating whether a shorter loan term (which reduces the risk of owing more than the car is worth) makes sense for you.<\/p>\n\n<h2>What credit score gets the best rate?<\/h2>\n<p>Lenders generally reserve their lowest advertised rates for borrowers with the strongest credit profiles \u2014 the exact score thresholds vary by lender, but broadly speaking, higher credit scores unlock meaningfully lower rates, while below-average credit typically means higher rates or the need for a larger down payment or co-signer.<\/p>\n\n<h2>Ways to reduce your total interest<\/h2>\n<ul>\n<li><b>Make a larger down payment<\/b> \u2014 directly reduces the principal financed and shrinks the interest calculated on it.<\/li>\n<li><b>Choose a shorter term<\/b> \u2014 higher monthly payment, but substantially less total interest.<\/li>\n<li><b>Make extra payments<\/b> \u2014 even modest recurring extra payments compound into meaningful interest savings, as shown in the simulator above.<\/li>\n<li><b>Shop your rate before visiting the dealer<\/b> \u2014 pre-approval from a bank or credit union gives you a benchmark against dealer financing offers.<\/li>\n<li><b>Avoid unnecessary add-ons<\/b> \u2014 extended warranties and other bundled products increase the amount financed and the interest paid on them.<\/li>\n<\/ul>\n\n<h2>Common auto financing mistakes<\/h2>\n<p>Common pitfalls include focusing only on the monthly payment without checking the total cost, stretching to a 72\u201384 month term to make an unaffordable car \"fit\" a budget, rolling negative equity from a trade-in into a new loan without accounting for it, and skipping comparison shopping between dealer financing and outside lenders.<\/p>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/loan\">Loan Calculator<\/a> for general amortization, the <a href=\"#\/calculator\/mortgage\">Mortgage Calculator<\/a> for home financing, and the <a href=\"#\/calculator\/compound-interest\">Compound Interest Calculator<\/a> for related interest math.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How is an auto loan calculated?<\/summary><p>The amount financed (price plus tax and fees, minus down payment and trade-in equity) is run through the standard amortization formula using your rate and term.<\/p><\/details>\n<details><summary>What is APR?<\/summary><p>Annual Percentage Rate \u2014 the interest rate plus certain fees, expressed as one yearly percentage for a more complete cost comparison between loan offers.<\/p><\/details>\n<details><summary>How much should I put down?<\/summary><p>There's no universal rule, but a larger down payment reduces both your payment and total interest \u2014 the down payment simulator above shows the exact effect of any amount you enter.<\/p><\/details>\n<details><summary>Is a 72-month loan a good idea?<\/summary><p>It lowers the monthly payment but increases total interest and raises the risk of owing more than the car is worth for longer \u2014 worth weighing against a shorter term using the term comparison table above.<\/p><\/details>\n<details><summary>Can I pay off my car loan early?<\/summary><p>Usually yes \u2014 check your loan agreement for any prepayment penalty first, then use the extra-payment simulator above to see your specific savings.<\/p><\/details>\n<details><summary>Does a larger down payment reduce interest?<\/summary><p>Yes \u2014 it directly reduces the principal financed, which lowers the interest calculated on it throughout the loan.<\/p><\/details>\n<details><summary>Should I buy new or used?<\/summary><p>New cars often get lower rates and full warranties; used cars cost less upfront and have already absorbed the steepest depreciation \u2014 the right choice depends on your budget and priorities.<\/p><\/details>\n<details><summary>What credit score gets the best rate?<\/summary><p>Generally, the strongest credit profiles unlock a lender's lowest advertised rates \u2014 exact thresholds vary by lender.<\/p><\/details>\n<details><summary>What is negative equity?<\/summary><p>Owing more on your current vehicle than it's worth \u2014 trading in with negative equity rolls that shortfall into your new loan, increasing what you finance.<\/p><\/details>\n<details><summary>How does trade-in affect my loan?<\/summary><p>Positive trade-in equity reduces your amount financed; negative equity increases it \u2014 enter both your trade-in value and amount owed above to see your net effect.<\/p><\/details>\n<details><summary>What fees should I expect?<\/summary><p>Common ones include sales tax, registration, title, and documentation fees \u2014 all included as separate inputs in this calculator.<\/p><\/details>\n<details><summary>Is leasing cheaper than buying?<\/summary><p>Often cheaper month-to-month and over a single lease term, but you build no ownership equity \u2014 compare your specific numbers in the lease-vs-buy section above.<\/p><\/details>\n<details><summary>How do extra payments help?<\/summary><p>They reduce principal directly, lowering the interest calculated on all remaining payments \u2014 see your exact interest and time saved in the simulator above.<\/p><\/details>\n<details><summary>Should I refinance my auto loan?<\/summary><p>Potentially, if rates have dropped or your credit has improved since your original loan \u2014 compare your current terms against a new offer's rate and remaining term before deciding.<\/p><\/details>\n<details><summary>How is sales tax applied to a car purchase?<\/summary><p>Typically as a percentage of the vehicle price (rules vary by location, and some places tax only the amount above trade-in credit) \u2014 this calculator applies it to the vehicle price directly.<\/p><\/details>\n<details><summary>Does a co-signer help me get a better rate?<\/summary><p>A co-signer with strong credit can sometimes help secure approval or a better rate, though they also take on equal responsibility for the debt.<\/p><\/details>\n<details><summary>What happens if I miss a car payment?<\/summary><p>Typically late fees, potential credit score damage, and eventually risk of repossession since the vehicle is collateral \u2014 specific consequences vary by lender.<\/p><\/details>\n<details><summary>Can I finance taxes and fees into my auto loan?<\/summary><p>Yes, commonly \u2014 this calculator includes them directly in the amount financed by default.<\/p><\/details>\n<details><summary>Is GAP insurance worth it?<\/summary><p>It covers the gap between what you owe and the vehicle's value if it's totaled \u2014 most useful when you have little or negative equity, such as with a small down payment or a rolled-in trade-in balance.<\/p><\/details>\n<details><summary>What is loan-to-value (LTV)?<\/summary><p>The ratio of your loan amount to the vehicle's value \u2014 a higher LTV (financing close to or more than the car is worth) generally carries more lender risk and sometimes a higher rate.<\/p><\/details>\n<details><summary>How much does a car really cost to own?<\/summary><p>Beyond the loan payment: insurance, fuel, maintenance, and depreciation all add substantially \u2014 see the true cost of ownership section above for a fuller picture.<\/p><\/details>\n<details><summary>Does vehicle depreciation affect my loan?<\/summary><p>Indirectly \u2014 if a car depreciates faster than you pay down the loan, you can end up \"underwater\" (owing more than it's worth), which matters if you plan to trade in or sell before payoff.<\/p><\/details>`,\n\ninterest:`\n<h2>Simple interest calculator \u2014 the complete resource<\/h2>\n<p>This calculator goes beyond a single number. It calculates forward (interest from principal, rate and time), works in reverse (find the principal, rate, or time from the other two), compares simple interest against compound interest, and breaks results down by day, week, month, quarter, and year \u2014 plus rate and time-period comparison tables and a full year-by-year schedule.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Simple interest formula<\/div>\n  <div class=\"f-eq\">I = P \u00d7 r \u00d7 t<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Total amount<\/div>\n  <div class=\"f-eq\">A = P + I<\/div>\n<\/div>\n<p><b>I<\/b> is the interest earned, <b>P<\/b> is the principal (starting amount), <b>r<\/b> is the annual interest rate (as a decimal \u2014 5% becomes 0.05), and <b>t<\/b> is time in years. <b>A<\/b>, the total amount, is simply the principal plus the interest earned.<\/p>\n\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">$5,000 at 5% for 3 years earns 5,000 \u00d7 0.05 \u00d7 3 = <b>$750<\/b> in interest, for a total value of $5,750.<\/p>\n<\/div>\n\n<h2>What is simple interest?<\/h2>\n<p>Simple interest is calculated only on the original principal \u2014 never on interest that has already accrued. This makes it grow in a straight line over time, unlike compound interest, which curves upward because each period's interest starts earning interest of its own.<\/p>\n\n<h2>Where simple interest is used<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Use case<\/th><th>Notes<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Short-term personal loans<\/td><td>Often calculated with simple interest for straightforward terms<\/td><\/tr>\n    <tr><td>Certain bonds and treasury bills<\/td><td>Some fixed-income instruments use simple interest structures<\/td><\/tr>\n    <tr><td>Educational examples<\/td><td>The clearest way to teach the core concept of interest<\/td><\/tr>\n    <tr><td>Some auto and consumer loans<\/td><td>Though many use amortizing\/reducing-balance methods instead \u2014 check your specific loan terms<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>Most everyday financial products you interact with \u2014 savings accounts, mortgages, credit cards \u2014 actually use compound interest, not simple interest. Understanding both, and knowing which applies to your situation, is essential before signing any loan or investment agreement.<\/p>\n\n<h2>Advantages and disadvantages<\/h2>\n<p><b>As a borrower<\/b>, simple interest is generally more favorable than compound interest \u2014 you're never charged interest on previously accrued interest, keeping the total cost lower and easier to predict. <b>As a saver or investor<\/b>, the reverse is true: compound interest grows your money faster over time, since your returns start generating their own returns.<\/p>\n\n<h2>Simple interest vs. compound interest<\/h2>\n<p>The core difference: simple interest is calculated only on the principal every period; compound interest is calculated on the principal <em>plus<\/em> all previously accumulated interest. Over short periods the difference is small, but it widens substantially over longer time horizons \u2014 the comparison mode above shows this gap directly for your own numbers, and the growth chart makes the straight-line-vs-curve difference visually obvious.<\/p>\n\n<h2>365-day vs. 360-day conventions<\/h2>\n<p>When calculating a daily interest rate, some financial institutions divide the annual rate by 365 days, while others use 360 days (sometimes called the \"Banker's Rule,\" historically used for simpler manual calculation). Using 360 days produces a very slightly higher daily rate than using 365 \u2014 a minor but real difference worth checking for precise short-term interest calculations, which is why this calculator lets you choose either convention.<\/p>\n\n<h2>Reverse calculations \u2014 finding P, rate, or time<\/h2>\n<p>The same formula (I = P \u00d7 r \u00d7 t) can be rearranged to solve for any of its variables if you know the other two:<\/p>\n<div class=\"formula-box\"><div class=\"f-label\">Find principal<\/div><div class=\"f-eq\">P = I \u00f7 (r \u00d7 t)<\/div><\/div>\n<div class=\"formula-box\"><div class=\"f-label\">Find rate<\/div><div class=\"f-eq\">r = I \u00f7 (P \u00d7 t)<\/div><\/div>\n<div class=\"formula-box\"><div class=\"f-label\">Find time<\/div><div class=\"f-eq\">t = I \u00f7 (P \u00d7 r)<\/div><\/div>\n<p>This is useful for questions like \"how much would I need to invest to earn $5,000 in interest?\" or \"what rate would I need to reach my goal in 3 years?\" \u2014 the reverse-calculator mode above handles all three directly.<\/p>\n\n<h2>Common mistakes<\/h2>\n<ul>\n<li><b>Forgetting to convert the rate to a decimal<\/b> \u2014 using \"5\" instead of \"0.05\" in the formula gives an answer 100 times too large.<\/li>\n<li><b>Mismatching time units<\/b> \u2014 the formula needs time in years; convert months or days first (or let this calculator do it for you).<\/li>\n<li><b>Assuming a loan uses simple interest without checking<\/b> \u2014 many consumer loans actually use amortizing (reducing-balance) interest, which behaves differently despite a similar-sounding name.<\/li>\n<li><b>Ignoring day-count convention differences<\/b> \u2014 365 vs. 360-day calculations produce slightly different daily rates, which can matter for precise short-term figures.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/compound-interest\">Compound Interest Calculator<\/a> for a full compounding breakdown, the <a href=\"#\/calculator\/loan\">Loan Calculator<\/a> and <a href=\"#\/calculator\/mortgage\">Mortgage Calculator<\/a> for amortizing loans, and the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for related math.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is simple interest?<\/summary><p>Interest calculated only on the original principal, never on previously accrued interest \u2014 it grows in a straight line rather than compounding.<\/p><\/details>\n<details><summary>How is simple interest calculated?<\/summary><p>Using I = P \u00d7 r \u00d7 t, where P is principal, r is the annual rate as a decimal, and t is time in years.<\/p><\/details>\n<details><summary>What is the formula?<\/summary><p>I = P \u00d7 r \u00d7 t for interest earned; A = P + I for the total amount including principal.<\/p><\/details>\n<details><summary>How is it different from compound interest?<\/summary><p>Compound interest calculates each period's interest on the principal plus all previously accumulated interest, causing it to grow faster over time than simple interest's straight-line growth.<\/p><\/details>\n<details><summary>Can simple interest be calculated monthly?<\/summary><p>Yes \u2014 convert your time period to years first (e.g., 6 months = 0.5 years) or select \"months\" as the time unit above.<\/p><\/details>\n<details><summary>Does simple interest change over time?<\/summary><p>No \u2014 the rate stays constant and interest is always calculated on the same original principal, which is exactly what makes it \"simple\" and predictable.<\/p><\/details>\n<details><summary>What is the principal amount?<\/summary><p>The original sum of money \u2014 either invested (earning interest) or borrowed (owing interest) \u2014 before any interest is added.<\/p><\/details>\n<details><summary>What is APR?<\/summary><p>Annual Percentage Rate \u2014 a standardized yearly rate that can include certain fees, used for comparing loan or credit offers on a common basis.<\/p><\/details>\n<details><summary>Is simple interest better for borrowers?<\/summary><p>Generally yes \u2014 since interest never compounds on itself, the total cost is typically lower and easier to predict compared to compound interest on the same nominal rate.<\/p><\/details>\n<details><summary>Where is simple interest commonly used?<\/summary><p>Some short-term loans, certain bonds, and educational examples \u2014 though many everyday financial products (savings accounts, mortgages, credit cards) actually use compound interest instead.<\/p><\/details>\n<details><summary>Can I calculate interest for days?<\/summary><p>Yes \u2014 select \"days\" as the time unit above, and the effective daily-rate breakdown shows results using either a 365- or 360-day convention.<\/p><\/details>\n<details><summary>Why do some banks use 360 days?<\/summary><p>It's a historical convention (the \"Banker's Rule\") that simplified manual calculation \u2014 it produces a very slightly higher daily rate than a 365-day convention.<\/p><\/details>\n<details><summary>Is simple interest taxable?<\/summary><p>In most jurisdictions, interest income is generally taxable, though specific rules vary by country and account type \u2014 consult a tax professional for your specific situation.<\/p><\/details>\n<details><summary>Can I repay a simple interest loan early?<\/summary><p>Often yes, and since interest hasn't compounded, early repayment can meaningfully reduce total interest paid \u2014 check your specific loan's terms for any prepayment conditions.<\/p><\/details>\n<details><summary>Does inflation affect real returns?<\/summary><p>Yes \u2014 inflation erodes purchasing power over time, so a given nominal interest rate translates to a lower \"real\" (inflation-adjusted) return.<\/p><\/details>\n<details><summary>How do I calculate the interest rate?<\/summary><p>Rearrange the formula: r = I \u00f7 (P \u00d7 t) \u2014 or use the reverse-calculator mode above directly.<\/p><\/details>\n<details><summary>How do I find the principal?<\/summary><p>Rearrange the formula: P = I \u00f7 (r \u00d7 t) \u2014 or use the reverse-calculator mode above directly.<\/p><\/details>\n<details><summary>How do I find the time?<\/summary><p>Rearrange the formula: t = I \u00f7 (P \u00d7 r) \u2014 or use the reverse-calculator mode above directly.<\/p><\/details>\n<details><summary>Can simple interest be negative?<\/summary><p>Not in the traditional formula with positive inputs \u2014 but if used to model a depreciating value or a discount, a \"negative rate\" framing is sometimes used informally.<\/p><\/details>\n<details><summary>Is simple interest used for mortgages?<\/summary><p>Rarely in its pure form \u2014 most mortgages use amortizing (reducing-balance) interest, which is mathematically different despite some surface similarity; see the Mortgage Calculator for that structure specifically.<\/p><\/details>\n<details><summary>What's the difference between a simple interest loan and a reducing balance loan?<\/summary><p>A true simple interest loan calculates interest on the original principal for the full term; a reducing-balance (amortizing) loan recalculates interest each period on the remaining balance, which is how most auto loans and mortgages actually work.<\/p><\/details>\n<details><summary>How much does the interest rate affect long-term growth?<\/summary><p>Substantially \u2014 even a few percentage points of difference compounds into a large gap over many years, as shown in the rate-comparison table above.<\/p><\/details>`,\n\n'compound-interest':`\n<h2>Compound interest calculator \u2014 a complete investment planning tool<\/h2>\n<p>This calculator does more than project a future value. It handles recurring monthly contributions, seven compounding frequencies (including continuous), inflation-adjusted results, a goal-seeking mode that solves for the monthly contribution needed to hit a target, a Rule of 72 calculator, and side-by-side comparisons across interest rates and compounding frequencies.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Compound interest formula<\/div>\n  <div class=\"f-eq\">A = P \u00d7 (1 + r\/n)\u207f\u1d57<\/div>\n<\/div>\n<p><b>A<\/b> is the future value, <b>P<\/b> is the principal, <b>r<\/b> is the annual interest rate (as a decimal), <b>n<\/b> is the number of compounding periods per year, and <b>t<\/b> is time in years.<\/p>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">$10,000 at 6% compounded monthly for 10 years grows to roughly <b>$18,194<\/b> \u2014 about $285 more than the same rate compounded annually ($17,908), simply because interest starts earning interest sooner and more often.<\/p>\n<\/div>\n\n<h2>What is compound interest?<\/h2>\n<p>Compound interest is interest calculated on both the original principal <em>and<\/em> any interest already earned \u2014 unlike simple interest, which only ever applies to the original amount. This is why compound growth curves upward over time rather than following a straight line: each period's gains become part of the base that earns the next period's gains.<\/p>\n\n<h2>How compounding works, step by step<\/h2>\n<ol style=\"color:var(--text-soft);\">\n<li>Interest is calculated on the current balance for one compounding period.<\/li>\n<li>That interest is added to the balance.<\/li>\n<li>The next period's interest is calculated on this new, larger balance.<\/li>\n<li>This repeats for every period across the full term \u2014 the more periods, the more often this \"interest on interest\" effect compounds.<\/li>\n<\/ol>\n\n<h2>Compound vs. simple interest<\/h2>\n<p>Over short periods the difference between simple and compound interest is small; over long periods, it becomes substantial. The \"simple vs. compound\" comparison above shows this directly for your own numbers \u2014 and it's exactly why compound growth is central to long-term investing, while simple interest is more often associated with short-term loans.<\/p>\n\n<h2>The Rule of 72<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Rule of 72<\/div>\n  <div class=\"f-eq\">Years to double \u2248 72 \u00f7 Annual rate<\/div>\n<\/div>\n<p>This quick mental-math shortcut estimates how long it takes an investment to double at a given annual rate \u2014 for example, at 8% annual growth, money doubles in roughly 72 \u00f7 8 = 9 years. It's an approximation (most accurate for rates between roughly 6% and 10%), not an exact calculation, but it's a genuinely useful way to build intuition about compounding speed. The Rule of 72 mode above runs this both directions \u2014 rate to years, or years to required rate.<\/p>\n\n<h2>The effect of compounding frequency<\/h2>\n<p>More frequent compounding (daily vs. monthly vs. annually) increases returns at the same stated annual rate, since interest has more opportunities to start earning its own interest. However, the improvement shrinks as frequency increases \u2014 the jump from annual to monthly compounding is meaningfully larger than the jump from daily to continuous compounding, which are nearly identical. The frequency-comparison table above shows this diminishing-returns pattern directly.<\/p>\n\n<h2>The power of starting early<\/h2>\n<p>Because compounding accelerates over time, money invested earlier generally has a larger long-term impact than a larger amount invested later \u2014 the extra years of compounding often matter more than the extra dollars. This is one of the most consistently repeated pieces of investing wisdom precisely because the math behind it is so reliable.<\/p>\n\n<h2>Recurring contributions<\/h2>\n<p>Regularly adding to an investment \u2014 even modest amounts \u2014 meaningfully accelerates growth, since every new contribution also gets the benefit of compounding for whatever time remains. This calculator applies your monthly contribution at the effective monthly rate implied by your chosen compounding frequency, and the contribution simulator above lets you see the impact of increasing (or decreasing) that monthly amount instantly.<\/p>\n\n<h2>Inflation and real returns<\/h2>\n<p>A future value in nominal dollars overstates your actual purchasing power if inflation is ignored \u2014 $50,000 in 10 years won't buy what $50,000 buys today. The inflation adjustment above divides your projected future value by the compounded inflation rate over the same period, giving a \"real\" (inflation-adjusted) estimate of what that future balance is actually worth in today's purchasing power.<\/p>\n\n<h2>Taxes and fees<\/h2>\n<p>Investment taxes (on interest, dividends, or capital gains) and fees (expense ratios, management charges) both reduce the growth this calculator projects, since neither is factored into the raw compound interest formula. Over long horizons even seemingly small annual fees compound into a substantial drag on final value \u2014 always check the net-of-fee return you're actually likely to receive, not just the headline rate.<\/p>\n\n<h2>Common investing mistakes<\/h2>\n<ul>\n<li><b>Underestimating the value of starting early<\/b> \u2014 delaying by even a few years can cost more than most people expect, given how compounding accelerates.<\/li>\n<li><b>Ignoring fees<\/b> \u2014 a 1-2% annual expense ratio sounds small but compounds into a meaningful reduction over decades.<\/li>\n<li><b>Forgetting inflation<\/b> \u2014 a large nominal future value can still represent disappointing real purchasing power.<\/li>\n<li><b>Assuming a fixed guaranteed rate<\/b> \u2014 actual investment returns vary year to year; this calculator (like any) shows a projection based on an assumed constant rate, not a guarantee.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/interest\">Simple Interest Calculator<\/a> for straight-line growth, the <a href=\"#\/calculator\/retirement\">Retirement Calculator<\/a> for retirement-specific projections, the <a href=\"#\/calculator\/investment\">Investment Calculator<\/a>, and the <a href=\"#\/calculator\/loan\">Loan Calculator<\/a> for the borrowing side of compounding.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is compound interest?<\/summary><p>Interest calculated on both the principal and previously accumulated interest, causing balances to grow faster than simple interest over time.<\/p><\/details>\n<details><summary>How is compound interest calculated?<\/summary><p>Using A = P \u00d7 (1 + r\/n)\u207f\u1d57, where A is the future value, P the principal, r the annual rate, n the compounding periods per year, and t the time in years.<\/p><\/details>\n<details><summary>What is the formula?<\/summary><p>A = P(1 + r\/n)\u207f\u1d57 for lump-sum growth; recurring contributions add an annuity component on top of this base formula.<\/p><\/details>\n<details><summary>What is compounding frequency?<\/summary><p>How often interest is calculated and added to the balance \u2014 annually, monthly, daily, or continuously are common options, each affecting the final value at the same stated rate.<\/p><\/details>\n<details><summary>Daily vs. monthly compounding \u2014 which is better?<\/summary><p>Daily compounding produces a very slightly higher return than monthly at the same nominal rate, though the difference is usually small \u2014 see the frequency comparison table above for your specific numbers.<\/p><\/details>\n<details><summary>What is continuous compounding?<\/summary><p>The theoretical limit of compounding frequency \u2014 as if interest compounds infinitely often \u2014 calculated with A = Pe^(rt); in practice it's nearly identical to daily compounding.<\/p><\/details>\n<details><summary>How often should interest compound?<\/summary><p>More frequent compounding is always at least as good for an investor (never worse), though the practical difference becomes small beyond monthly or daily.<\/p><\/details>\n<details><summary>Is compound interest better than simple interest?<\/summary><p>For investors and savers, yes \u2014 it grows wealth faster. For borrowers, simple interest is generally cheaper, since interest never compounds on itself.<\/p><\/details>\n<details><summary>What is the Rule of 72?<\/summary><p>A quick approximation \u2014 divide 72 by the annual rate to estimate years to double an investment (or divide 72 by years to estimate the required rate).<\/p><\/details>\n<details><summary>How long does it take to double my money?<\/summary><p>Use the Rule of 72 mode above \u2014 for example, at 6% annual growth, money doubles in roughly 12 years (72 \u00f7 6).<\/p><\/details>\n<details><summary>How do recurring deposits affect growth?<\/summary><p>They accelerate it substantially \u2014 each contribution compounds for whatever time remains, and consistent contributions often matter more than the initial lump sum over long horizons.<\/p><\/details>\n<details><summary>What happens if I increase my monthly investment?<\/summary><p>Future value increases \u2014 use the contribution simulator above to see the exact effect of any monthly amount on your specific numbers.<\/p><\/details>\n<details><summary>How does inflation affect future value?<\/summary><p>It erodes real purchasing power \u2014 a large nominal future value can represent a smaller \"real\" value once adjusted for inflation, shown in the inflation-adjusted result above.<\/p><\/details>\n<details><summary>How do taxes reduce returns?<\/summary><p>Taxes on interest, dividends, or capital gains reduce the amount that actually stays invested and compounds \u2014 this calculator projects pre-tax growth, so actual after-tax results will be lower.<\/p><\/details>\n<details><summary>Can I calculate retirement savings with this?<\/summary><p>Yes, approximately \u2014 enter your current savings, expected rate, years until retirement, and monthly contribution; see the dedicated Retirement Calculator for a more retirement-specific breakdown.<\/p><\/details>\n<details><summary>Can I model regular investment contributions like a SIP?<\/summary><p>Yes \u2014 the monthly contribution field models exactly this kind of systematic, regular investing pattern.<\/p><\/details>\n<details><summary>What is APY vs. APR?<\/summary><p>APR is a simple annualized rate; APY (Annual Percentage Yield) accounts for compounding within the year, so APY is generally slightly higher than APR at the same nominal rate \u2014 the \"effective annual yield\" shown above is essentially the APY for your inputs.<\/p><\/details>\n<details><summary>Why do fees matter so much over time?<\/summary><p>Because fees compound too \u2014 a seemingly small annual fee reduces the base that grows every single year, and that reduction compounds alongside your returns, creating a larger cumulative drag than the headline percentage suggests.<\/p><\/details>\n<details><summary>What is effective annual rate?<\/summary><p>The actual annual growth rate once compounding frequency is accounted for \u2014 always equal to or higher than the nominal stated rate for any compounding frequency more often than annual.<\/p><\/details>\n<details><summary>Is compound interest guaranteed?<\/summary><p>No \u2014 this calculator projects growth assuming a constant rate you specify; real investments fluctuate and returns are never guaranteed, unlike a fixed-rate savings product where the rate itself may be contractually guaranteed.<\/p><\/details>\n<details><summary>How much does starting 5 years earlier really matter?<\/summary><p>Often substantially \u2014 because compounding accelerates over time, the years lost by starting later are frequently the highest-value years of the entire investment horizon.<\/p><\/details>\n<details><summary>Can I find the required contribution to hit a specific goal?<\/summary><p>Yes \u2014 switch to the goal mode above, enter your target amount, current savings, rate, and timeframe, and it solves for the required monthly contribution directly.<\/p><\/details>\n<details><summary>Does a higher compounding frequency always mean more money?<\/summary><p>At the same stated annual rate, yes, though the gains diminish quickly \u2014 moving from annual to monthly compounding matters much more than moving from monthly to daily.<\/p><\/details>\n<details><summary>What's a realistic annual return to assume?<\/summary><p>This varies enormously by asset type and risk level \u2014 this calculator doesn't recommend a specific rate; use a figure appropriate to your actual investment strategy and risk tolerance.<\/p><\/details>\n<details><summary>How is this different from a savings account calculator?<\/summary><p>It's not fundamentally different \u2014 a savings account earning compound interest follows the exact same formula, just typically at a lower, more stable rate than higher-risk investments.<\/p><\/details>\n<details><summary>Can compounding work against me?<\/summary><p>Yes \u2014 on debt. Credit card balances and some loans compound interest the same way, which is why unpaid balances can grow faster than expected; see the Loan Calculator for the borrowing side of this math.<\/p><\/details>`,\n\nretirement:`\n<h2>Retirement calculator \u2014 a complete planning dashboard<\/h2>\n<p>This calculator goes beyond a single projected balance. It includes a retirement readiness score, a withdrawal\/drawdown simulation estimating how long your savings will last, retirement-age and contribution simulators, investment-return and withdrawal-rate comparison tables, a dedicated FIRE (Financial Independence, Retire Early) calculator, and a retirement lifestyle expense planner.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Accumulation formula<\/div>\n  <div class=\"f-eq\">FV = PV(1+i)\u207f + PMT \u00d7 [ ((1+i)\u207f \u2212 1) \/ i ]<\/div>\n<\/div>\n<p>PV is your current savings, PMT is your monthly contribution, i is the monthly rate of return, and n is the number of months until retirement.<\/p>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">Starting with $20,000 at age 30, contributing $400\/month at a 7% expected annual return until age 65, projects to roughly <b>$898,000<\/b> at retirement \u2014 with about $188,000 of that from contributions and the remaining $710,000 from investment growth.<\/p>\n<\/div>\n\n<h2>What is retirement planning?<\/h2>\n<p>Retirement planning means estimating how much you'll need to sustain your desired lifestyle once you stop earning a regular income, and working backward to figure out how much to save and invest now to get there. It combines several moving pieces \u2014 current savings, ongoing contributions, expected investment growth, inflation, and eventual withdrawal needs \u2014 which is why this calculator models the full journey rather than just a single savings total.<\/p>\n\n<h2>How much do I need to retire?<\/h2>\n<p>A widely cited starting point is the <b>25\u00d7 rule<\/b>: multiply your desired annual retirement income by 25, which corresponds to a 4% initial withdrawal rate. Someone wanting $50,000\/year would target roughly $1,250,000. This is a general guideline, not a personalized recommendation \u2014 actual needs vary with lifestyle, health, other income sources, and market conditions, which is why the readiness score above compares your specific projection against this benchmark rather than a generic number.<\/p>\n\n<h2>The 4% rule and safe withdrawal rates<\/h2>\n<p>The 4% rule suggests that withdrawing 4% of your portfolio in the first year of retirement, then adjusting that dollar amount for inflation each subsequent year, has historically had a strong chance of lasting through a ~30-year retirement without depleting the portfolio. It's a useful starting heuristic, not a guarantee \u2014 the withdrawal strategy comparison above shows 3%, 4%, and 5% side by side, since more conservative or aggressive rates each carry different depletion risk.<\/p>\n\n<h2>What is FIRE?<\/h2>\n<p><b>FIRE<\/b> (Financial Independence, Retire Early) is a movement centered on aggressive saving and investing to reach financial independence well before traditional retirement age. This calculator's FIRE tab computes your <b>FIRE number<\/b> (25\u00d7 annual expenses), plus <b>Lean FIRE<\/b> (a leaner target lifestyle), <b>Fat FIRE<\/b> (a more comfortable target), and <b>Coast FIRE<\/b> \u2014 the amount you'd need saved today such that, with no further contributions, compounding alone would carry you to your FIRE number by a traditional retirement age.<\/p>\n\n<h2>Inflation and retirement<\/h2>\n<p>Inflation steadily erodes purchasing power, so a retirement income that feels comfortable today may not stretch as far decades from now. This calculator inflates your stated \"desired annual retirement income\" forward to the dollars you'll actually be dealing with at retirement, and continues inflating your withdrawal amount each year throughout the drawdown simulation \u2014 a more realistic picture than assuming flat, unchanging expenses.<\/p>\n\n<h2>The power of starting early<\/h2>\n<p>Because investment growth compounds, money invested earlier has more time to benefit from that compounding \u2014 often making the starting age a bigger lever than the contribution amount itself. Use the retirement-age simulator above to see directly how retiring a few years earlier or later shifts your final balance, holding everything else constant.<\/p>\n\n<h2>Asset allocation and diversification basics<\/h2>\n<p>This calculator assumes a single constant expected return, but real portfolios are typically diversified across asset classes (stocks, bonds, and others) with different risk and return characteristics \u2014 usually shifting toward more conservative allocations as retirement approaches, since there's less time to recover from a market downturn. The \"expected annual return\" you enter should reflect a reasonable long-term estimate for your actual intended asset mix, not an optimistic best case.<\/p>\n\n<h2>Should I include Social Security or a pension?<\/h2>\n<p>If you expect other guaranteed retirement income (like Social Security or a pension), it reduces how much your personal savings alone need to cover \u2014 consider netting that expected income against your \"desired annual retirement income\" input for a more accurate readiness picture.<\/p>\n\n<h2>Common retirement planning mistakes<\/h2>\n<ul>\n<li><b>Starting late<\/b> \u2014 delaying by even a few years meaningfully reduces the benefit of compounding over the remaining timeline.<\/li>\n<li><b>Ignoring inflation<\/b> \u2014 a retirement income target that only makes sense in today's dollars understates what you'll actually need decades from now.<\/li>\n<li><b>Assuming an overly optimistic return<\/b> \u2014 using an aggressive expected return can make a shortfall look like a surplus.<\/li>\n<li><b>Withdrawing too aggressively early in retirement<\/b> \u2014 an initial withdrawal rate that's too high raises the risk of depleting savings before the end of retirement, especially if early market returns are poor.<\/li>\n<li><b>Not adjusting the plan over time<\/b> \u2014 a retirement plan isn't \"set and forget\"; revisit it periodically as your income, goals, and market conditions change.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/compound-interest\">Compound Interest Calculator<\/a> for general investment growth, the <a href=\"#\/calculator\/investment\">Investment Calculator<\/a>, and the <a href=\"#\/calculator\/interest\">Simple Interest Calculator<\/a> for related interest math.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How much money do I need to retire?<\/summary><p>A common starting benchmark is 25 times your desired annual retirement income (supporting roughly a 4% withdrawal rate) \u2014 though your true number depends on lifestyle, other income sources, and how long retirement needs to last.<\/p><\/details>\n<details><summary>What is a retirement calculator?<\/summary><p>A tool that projects how your current savings and contributions will grow by retirement, and estimates whether that projected balance can sustainably support your desired retirement income.<\/p><\/details>\n<details><summary>How is retirement income estimated?<\/summary><p>Typically as a percentage (withdrawal rate) of your projected balance at retirement \u2014 this calculator compares 3%, 4%, and 5% withdrawal rates directly.<\/p><\/details>\n<details><summary>What is the 4% rule?<\/summary><p>A widely cited guideline suggesting an initial 4% withdrawal, adjusted for inflation each year after, has historically had a strong chance of lasting a ~30-year retirement \u2014 a heuristic, not a guarantee.<\/p><\/details>\n<details><summary>What is FIRE?<\/summary><p>Financial Independence, Retire Early \u2014 a savings-focused approach aimed at reaching financial independence well ahead of traditional retirement age, calculated in the FIRE tab above.<\/p><\/details>\n<details><summary>How much should I save each month?<\/summary><p>It depends on your goal, timeline, and expected return \u2014 use the monthly contribution simulator above to see how different amounts affect your projected balance.<\/p><\/details>\n<details><summary>When should I start saving for retirement?<\/summary><p>As early as possible \u2014 because of compounding, the years matter more than most people expect; the retirement-age simulator above shows this trade-off directly.<\/p><\/details>\n<details><summary>How does inflation affect retirement?<\/summary><p>It erodes purchasing power over time, meaning a fixed nominal income buys less each year \u2014 this calculator inflates your withdrawal target throughout the drawdown simulation to reflect this.<\/p><\/details>\n<details><summary>What investment return should I assume?<\/summary><p>This varies by portfolio and risk tolerance \u2014 this calculator doesn't recommend a specific figure; use a reasonable long-term estimate appropriate to your actual asset allocation.<\/p><\/details>\n<details><summary>Can I retire at 55?<\/summary><p>Possibly, depending on your savings rate, expected return, and target income \u2014 run your own numbers in the projection tab to see your specific outlook.<\/p><\/details>\n<details><summary>Can I retire with $500,000?<\/summary><p>It depends heavily on your desired annual income and withdrawal rate \u2014 at a 4% withdrawal rate, $500,000 supports roughly $20,000\/year before inflation adjustments; check the withdrawal comparison table above for your own numbers.<\/p><\/details>\n<details><summary>Can I retire with $1 million?<\/summary><p>At a 4% withdrawal rate, $1 million supports roughly $40,000\/year \u2014 whether that's sufficient depends entirely on your expected expenses and other income sources.<\/p><\/details>\n<details><summary>How long will my retirement savings last?<\/summary><p>This calculator's drawdown simulation estimates exactly this, based on your projected balance, desired income, expected return, and inflation assumptions.<\/p><\/details>\n<details><summary>How do taxes affect retirement income?<\/summary><p>Taxes reduce the amount actually available to spend \u2014 this calculator projects pre-tax figures, so your real spendable income will typically be lower depending on your account types and tax situation.<\/p><\/details>\n<details><summary>Should I include Social Security in my projection?<\/summary><p>Yes, generally \u2014 netting expected Social Security or pension income against your desired retirement income gives a more accurate picture of what your personal savings actually need to cover.<\/p><\/details>\n<details><summary>What is a safe withdrawal rate?<\/summary><p>The percentage of a portfolio that can be withdrawn annually with a reasonably low risk of depleting it over a typical retirement length \u2014 4% is the most commonly cited starting point, though 3-5% is a realistic range depending on assumptions.<\/p><\/details>\n<details><summary>What happens if investment returns are lower than expected?<\/summary><p>Your actual balance will fall short of the projection, and\/or your savings may not last as long during withdrawal \u2014 this is exactly why conservative return assumptions and periodic plan reviews matter.<\/p><\/details>\n<details><summary>Should I pay off debt before retirement?<\/summary><p>Generally advisable where feasible, since ongoing debt payments increase your required retirement income \u2014 though the right approach depends on the debt's interest rate versus your expected investment return.<\/p><\/details>\n<details><summary>How much emergency savings do retirees need?<\/summary><p>This varies by individual circumstances; many financial professionals suggest maintaining a liquid cash reserve separate from long-term investments to avoid withdrawing from investments during a market downturn.<\/p><\/details>\n<details><summary>Can I adjust my plan each year?<\/summary><p>Yes, and you generally should \u2014 revisit your assumptions periodically as your income, goals, and actual investment performance evolve over time.<\/p><\/details>\n<details><summary>What is Coast FIRE?<\/summary><p>The amount you'd need saved today such that, with no further contributions, compound growth alone would carry it to your FIRE number by a chosen future age.<\/p><\/details>\n<details><summary>What is Lean FIRE and Fat FIRE?<\/summary><p>Lean FIRE targets a leaner-than-current lifestyle (this calculator uses 70% of current expenses); Fat FIRE targets a more comfortable lifestyle (150% of current expenses) \u2014 both are variations on the same 25\u00d7 framework.<\/p><\/details>\n<details><summary>What is a retirement readiness score?<\/summary><p>A general indicator comparing your projected retirement balance against a target based on your desired income and the 25\u00d7 rule \u2014 a planning signal, not a certified financial assessment.<\/p><\/details>\n<details><summary>How accurate are retirement projections?<\/summary><p>They're estimates based on constant assumed rates \u2014 real markets fluctuate year to year, so treat any projection (including this one) as a planning tool, not a guarantee of future results.<\/p><\/details>\n<details><summary>Does this calculator account for taxes on withdrawals?<\/summary><p>No \u2014 it projects pre-tax figures; actual after-tax retirement income will typically be lower depending on your account types and tax situation.<\/p><\/details>\n<details><summary>What's the difference between accumulation and drawdown phases?<\/summary><p>Accumulation is the saving\/growing period before retirement; drawdown is the withdrawal period after retirement \u2014 this calculator models both phases separately.<\/p><\/details>\n<details><summary>How does retiring earlier affect my required savings rate?<\/summary><p>Significantly \u2014 fewer years to accumulate and more years of withdrawals both increase the savings rate needed; use the retirement-age simulator above to see the effect on your specific numbers.<\/p><\/details>`,\n\ninvestment:`\n<h2>Investment calculator \u2014 a complete planning center<\/h2>\n<p>This calculator does more than project a single future value. It computes ROI and CAGR, models fee drag, compares lump-sum versus monthly investing, includes a goal-based planner that solves for the required monthly contribution, and lets you compare three contribution scenarios side by side.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Compound growth with contributions<\/div>\n  <div class=\"f-eq\">FV = PV(1+i)\u207f + PMT \u00d7 [ ((1+i)\u207f \u2212 1) \/ i ]<\/div>\n<\/div>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">$5,000 invested initially, plus $250\/month for 15 years at an 8% annual return, grows to roughly <b>$100,262<\/b> \u2014 of which $50,000 is your own contributions and about $50,262 is investment growth.<\/p>\n<\/div>\n\n<h2>What is an investment calculator?<\/h2>\n<p>An investment calculator projects how a lump sum, regular contributions, or both might grow over time at an assumed rate of return \u2014 turning the abstract idea of \"investing for the future\" into a concrete, adjustable number you can plan around.<\/p>\n\n<h2>CAGR and ROI explained<\/h2>\n<p><b>ROI<\/b> (Return on Investment) expresses total growth as a percentage of total contributed \u2014 a simple, intuitive measure of overall gain. <b>CAGR<\/b> (Compound Annual Growth Rate) expresses that growth as a smooth, constant annual rate \u2014 useful for comparing investments across different time periods on an apples-to-apples basis, even though real returns actually vary year to year rather than growing at a perfectly steady rate.<\/p>\n\n<h2>Lump sum vs. monthly investing<\/h2>\n<p>Investing a lump sum immediately generally outperforms spreading the same total across monthly contributions, purely because the lump sum has more total time in the market compounding. That said, most people don't have a large lump sum sitting idle \u2014 consistent monthly investing from ongoing income is the realistic path for most investors, and the comparison above shows both approaches applied to your own numbers so you can see the trade-off clearly.<\/p>\n\n<h2>Dollar-cost averaging<\/h2>\n<p>Dollar-cost averaging means investing a fixed amount at regular intervals (like monthly contributions) regardless of whether prices are up or down \u2014 this naturally buys more shares when prices are low and fewer when prices are high, averaging out the purchase price over time. It's less about maximizing returns than about removing the difficulty of trying to time the market, and it's exactly the pattern this calculator's monthly-contribution feature models.<\/p>\n\n<h2>Why starting early matters<\/h2>\n<p>Because growth compounds, money invested earlier has more total time to benefit \u2014 often making the starting date a bigger lever on final outcomes than the contribution amount itself. The time-horizon comparison table above (5\/10\/20\/30\/40 years) makes this concrete: extending the same monthly contribution by another decade or two produces a disproportionately larger final balance, not just a proportionally larger one.<\/p>\n\n<h2>Investment fees and their long-term cost<\/h2>\n<p>Annual fees (expense ratios, management or advisory fees) reduce your effective return every single year, and that reduction compounds right alongside your returns \u2014 a 1-2% annual fee sounds small but can meaningfully erode a portfolio's value over multiple decades. Enter your fund's actual expense ratio in the fee field above to see this drag quantified directly against your own numbers.<\/p>\n\n<h2>Inflation and real returns<\/h2>\n<p>A large future balance in nominal dollars can still represent disappointing real purchasing power once inflation is factored in \u2014 the inflation-adjusted value shown above divides your projected balance by compounded inflation over the same period, giving a more honest sense of what that future money will actually buy in today's terms.<\/p>\n\n<h2>Taxes and investment returns<\/h2>\n<p>Depending on account type and jurisdiction, investment gains may be taxed as capital gains, dividends, or ordinary income \u2014 this calculator projects pre-tax growth, so your actual after-tax outcome will typically be lower. Tax-advantaged accounts (where available) can meaningfully improve real, spendable returns compared to a fully taxable account holding the same investments.<\/p>\n\n<h2>Asset allocation and diversification<\/h2>\n<p>Asset allocation refers to how a portfolio is split across asset classes (stocks, bonds, cash, and others), each with different risk and return characteristics. Diversification \u2014 spreading investments across many holdings rather than concentrating in a few \u2014 helps reduce the impact of any single investment performing poorly. The illustrative allocation chart above shows one generic example split, not a personalized recommendation; your own allocation should reflect your specific risk tolerance and timeline.<\/p>\n\n<h2>Risk vs. return<\/h2>\n<p>Generally, investments with higher expected returns carry higher risk (greater variability and potential for loss), while more conservative investments offer steadier but typically lower returns. The risk-profile selector above fills in an illustrative return range for conservative, moderate, and aggressive profiles \u2014 these are general educational ranges, not guarantees or personalized recommendations.<\/p>\n\n<h2>Common investing mistakes<\/h2>\n<ul>\n<li><b>Waiting to start<\/b> \u2014 delaying investing even a few years can cost more in lost compounding than most people expect.<\/li>\n<li><b>Ignoring fees<\/b> \u2014 a seemingly small annual expense ratio compounds into a real, substantial cost over decades.<\/li>\n<li><b>Assuming an unrealistically high return<\/b> \u2014 overly optimistic assumptions can make a shortfall look like a surplus until it's too late to adjust.<\/li>\n<li><b>Chasing short-term performance<\/b> \u2014 reacting to short-term market swings often undermines the long-term compounding this calculator is designed to project.<\/li>\n<li><b>Under-diversifying<\/b> \u2014 concentrating too heavily in a single investment increases risk without necessarily increasing expected return.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/compound-interest\">Compound Interest Calculator<\/a> for the underlying growth mechanics, the <a href=\"#\/calculator\/retirement\">Retirement Calculator<\/a> for retirement-specific planning, and the <a href=\"#\/calculator\/interest\">Simple Interest Calculator<\/a> for related interest math.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is an investment calculator?<\/summary><p>A tool that projects how an initial investment and\/or regular contributions might grow over time at an assumed rate of return.<\/p><\/details>\n<details><summary>How is investment growth calculated?<\/summary><p>Using compound interest applied to both the initial amount and ongoing contributions \u2014 see the formula above for the full calculation.<\/p><\/details>\n<details><summary>What is compound interest?<\/summary><p>Interest (or investment growth) calculated on both the original amount and all previously accumulated growth, causing balances to grow faster than simple, non-compounding growth over time.<\/p><\/details>\n<details><summary>What is CAGR?<\/summary><p>Compound Annual Growth Rate \u2014 the smoothed, constant annual growth rate that would produce the same overall result, useful for comparing investments across different time periods.<\/p><\/details>\n<details><summary>What is ROI?<\/summary><p>Return on Investment \u2014 total growth expressed as a percentage of total amount contributed.<\/p><\/details>\n<details><summary>How much should I invest monthly?<\/summary><p>It depends on your goals and timeline \u2014 use the goal-based planner above to solve for the monthly amount needed to reach a specific target.<\/p><\/details>\n<details><summary>How often should investments compound?<\/summary><p>More frequent compounding produces marginally higher returns at the same stated rate, though the practical difference beyond monthly compounding is typically small.<\/p><\/details>\n<details><summary>Should I reinvest dividends?<\/summary><p>Generally, reinvesting dividends accelerates compounding by putting that income back to work immediately rather than taking it as cash \u2014 though your own tax situation and cash-flow needs matter too.<\/p><\/details>\n<details><summary>How does inflation affect investments?<\/summary><p>It erodes purchasing power over time \u2014 the inflation-adjusted value above shows what your projected balance is actually worth in today's dollars.<\/p><\/details>\n<details><summary>How do fees reduce returns?<\/summary><p>Fees reduce your effective annual return every year, and that reduction compounds alongside your returns \u2014 see the fee-impact figure above for your specific inputs.<\/p><\/details>\n<details><summary>What annual return should I assume?<\/summary><p>This varies by asset class and risk level \u2014 this calculator doesn't recommend a specific figure; the risk-profile presets offer general illustrative ranges only.<\/p><\/details>\n<details><summary>What is dollar-cost averaging?<\/summary><p>Investing a fixed amount at regular intervals regardless of price \u2014 it averages your purchase cost over time and removes the difficulty of trying to time the market.<\/p><\/details>\n<details><summary>Is a lump sum better than monthly investing?<\/summary><p>Mathematically, a lump sum invested immediately generally ends up ahead, since it has more time to compound \u2014 though monthly investing is the realistic approach for most people building wealth from ongoing income.<\/p><\/details>\n<details><summary>How long does it take to double an investment?<\/summary><p>Roughly 72 \u00f7 annual return, per the Rule of 72 \u2014 see the Compound Interest Calculator for a dedicated Rule of 72 tool.<\/p><\/details>\n<details><summary>What is the Rule of 72?<\/summary><p>A quick approximation for estimating years to double an investment by dividing 72 by the annual growth rate.<\/p><\/details>\n<details><summary>What is a realistic investment return?<\/summary><p>This varies significantly by asset allocation, time period, and market conditions \u2014 there's no single universal answer, which is why this calculator lets you set your own assumption rather than prescribing one.<\/p><\/details>\n<details><summary>How do taxes affect my investment?<\/summary><p>Taxes on gains, dividends, or interest reduce the amount that stays invested and compounds \u2014 this calculator projects pre-tax growth, so actual after-tax results are typically lower.<\/p><\/details>\n<details><summary>What is diversification?<\/summary><p>Spreading investments across multiple holdings or asset classes to reduce the impact of any single investment performing poorly.<\/p><\/details>\n<details><summary>How much risk should I take?<\/summary><p>This depends on your personal timeline, goals, and comfort with volatility \u2014 the risk-profile presets above are general illustrative ranges, not personalized advice.<\/p><\/details>\n<details><summary>Can I calculate retirement savings with this tool?<\/summary><p>Yes, approximately \u2014 though the dedicated Retirement Calculator adds retirement-specific features like a withdrawal\/drawdown simulation and readiness scoring.<\/p><\/details>\n<details><summary>What's the difference between this and the Compound Interest Calculator?<\/summary><p>They share the same underlying math; this calculator adds investment-specific framing like CAGR\/ROI, fee impact, lump-sum comparisons, and scenario planning.<\/p><\/details>\n<details><summary>Can I compare multiple contribution amounts at once?<\/summary><p>Yes \u2014 switch to the 3-scenario comparison mode above to see up to three different monthly contribution levels side by side.<\/p><\/details>\n<details><summary>Does this account for market volatility?<\/summary><p>No \u2014 it projects growth at a single constant assumed rate; real markets fluctuate year to year, so treat any projection as a planning estimate, not a guarantee.<\/p><\/details>\n<details><summary>What happens if my actual return is lower than assumed?<\/summary><p>Your actual balance will fall short of the projection \u2014 it's often wise to run the return-comparison table above with a conservative rate alongside your expected rate.<\/p><\/details>\n<details><summary>Is it better to invest a windfall immediately or spread it out?<\/summary><p>Investing immediately (lump sum) has historically outperformed spreading a windfall out over time in most market conditions, though spreading it out can reduce the risk of investing everything right before a downturn \u2014 a personal risk-tolerance decision as much as a mathematical one.<\/p><\/details>\n<details><summary>How does an expense ratio affect my long-term returns?<\/summary><p>It directly reduces your net annual return every year, and since that reduction compounds too, even a 1% expense ratio can meaningfully lower your final balance over multiple decades \u2014 see the fee comparison above for your own numbers.<\/p><\/details>\n<details><summary>What's a good ROI for a long-term investment?<\/summary><p>This varies enormously by asset class, time horizon, and market conditions \u2014 there's no single universal benchmark, and ROI should generally be considered alongside CAGR and risk level.<\/p><\/details>\n<details><summary>Should I increase my contributions over time?<\/summary><p>Many people do, often in line with income growth \u2014 this calculator uses a flat monthly contribution, so model an increased amount by adjusting the monthly contribution field to reflect a later stage of your plan.<\/p><\/details>\n<details><summary>How accurate are long-term investment projections?<\/summary><p>They're estimates based on a constant assumed rate \u2014 genuinely useful for planning purposes, but real returns vary year to year, so treat any single projected number as a scenario, not a certainty.<\/p><\/details>\n<details><summary>Can this calculator help me choose between investment options?<\/summary><p>It can help you compare the numerical outcomes of different contribution amounts, rates, or time periods, but it doesn't recommend specific investments or account types.<\/p><\/details>\n<details><summary>What's the benefit of starting to invest in your 20s vs. your 30s?<\/summary><p>Often substantial \u2014 the extra decade of compounding frequently outweighs a considerably larger contribution started later, which is the core reason \"start early\" is such consistent investing advice.<\/p><\/details>\n<details><summary>Does this calculator include Social Security or pension income?<\/summary><p>No \u2014 it projects investment growth only; see the Retirement Calculator for a tool that can factor in other retirement income sources.<\/p><\/details>`,\n\namortization:`\n<h2>Amortization calculator \u2014 a complete loan repayment analysis tool<\/h2>\n<p>This calculator builds a genuinely complete amortization schedule \u2014 every payment from the first to the last, searchable and exportable \u2014 plus an extra-payment simulator, payment-frequency and loan-term comparison tables, an interest-rate comparison, and a refinance analysis comparing your current loan against a hypothetical new one.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Each period<\/div>\n  <div class=\"f-eq\">Interest = Balance \u00d7 periodic rate &nbsp;|&nbsp; Principal = Payment \u2212 Interest &nbsp;|&nbsp; New balance = Balance \u2212 Principal<\/div>\n<\/div>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">On a $240,000 loan at 6.25% over 30 years, the first month's payment of about $1,478 includes roughly $1,250 in interest and only $228 toward principal \u2014 that ratio gradually flips over the loan's life, until the final payments are almost entirely principal.<\/p>\n<\/div>\n\n<h2>What is amortization?<\/h2>\n<p>Amortization is the process of paying off a loan through regular, fixed payments that combine both interest and principal, structured so the balance reaches exactly zero at the end of the term. Each payment is the same amount, but the split between interest and principal shifts every period as the balance declines.<\/p>\n\n<h2>How amortization works, step by step<\/h2>\n<ol style=\"color:var(--text-soft);\">\n<li>Calculate interest for the period: outstanding balance \u00d7 periodic interest rate.<\/li>\n<li>Subtract that interest from the fixed payment \u2014 the remainder is principal.<\/li>\n<li>Subtract the principal portion from the balance to get the new, lower balance.<\/li>\n<li>Repeat for every remaining period \u2014 as the balance shrinks, less of each payment goes to interest and more goes to principal.<\/li>\n<\/ol>\n\n<h2>Why are early payments mostly interest?<\/h2>\n<p>Interest is calculated on the current outstanding balance, which is largest at the very start of the loan \u2014 so naturally, the interest portion of each payment starts high and gradually declines as the balance shrinks. This is a mathematical consequence of how amortization works, not a sign of an unfavorable loan.<\/p>\n\n<h2>Amortization schedules explained<\/h2>\n<p>A full amortization schedule lists every single payment over the life of the loan, showing the interest, principal, and remaining balance at each step. The \"complete amortization schedule\" section above provides exactly this \u2014 searchable by payment number, viewable as a full list or an annual summary, and downloadable as a CSV for spreadsheet analysis.<\/p>\n\n<h2>Fixed vs. variable-rate loans<\/h2>\n<p>This calculator assumes a fixed interest rate, meaning the rate (and therefore the payment) stays constant for the full term \u2014 the standard assumption for most amortization tools. A variable-rate loan's payment and interest split can change over time as the rate adjusts, making its schedule harder to predict in advance.<\/p>\n\n<h2>Extra payments and early payoff strategies<\/h2>\n<p>Extra payments go directly toward principal, which reduces the balance interest is calculated on for every remaining period \u2014 this compounding effect means even modest, consistent extra payments can meaningfully cut both total interest and the loan's payoff time. The extra-payment simulator above shows the exact interest and time saved for any amount you enter, specific to your loan.<\/p>\n\n<h2>How payment frequency affects your loan<\/h2>\n<p>Switching from monthly to bi-weekly or weekly payments slightly reduces total interest, since principal gets reduced marginally more often. The frequency comparison table above shows this effect is generally smaller than people expect \u2014 the interest rate and loan term matter far more than payment frequency alone.<\/p>\n\n<h2>Refinancing \u2014 when does it make sense?<\/h2>\n<p>Refinancing replaces your current loan with a new one, ideally at a lower rate or more favorable term. It's worth it when the monthly savings, multiplied over time, outweigh the closing costs of the new loan \u2014 the refinance analysis above calculates this directly, including a break-even point showing how many months it takes for the savings to cover the closing costs.<\/p>\n\n<h2>Balloon payments<\/h2>\n<p>A balloon payment is a large lump-sum payment due at the end of a loan term that's smaller than a fully amortizing schedule would otherwise require \u2014 common in some commercial and short-term loans. The loan is structured with lower regular payments throughout, but the borrower must pay off (or refinance) a substantial remaining balance at the end.<\/p>\n\n<h2>Negative amortization<\/h2>\n<p>Negative amortization occurs when a payment is smaller than the interest accruing for that period, causing the loan balance to actually grow instead of shrink \u2014 the unpaid interest gets added to the principal. This is uncommon in standard fixed-rate loans but can occur with certain adjustable-rate or minimum-payment loan structures, and is generally something borrowers should understand clearly before agreeing to such terms.<\/p>\n\n<h2>Common borrowing mistakes<\/h2>\n<ul>\n<li><b>Only comparing monthly payments<\/b> \u2014 a lower payment from a longer term often means significantly more total interest.<\/li>\n<li><b>Not checking for prepayment penalties<\/b> \u2014 confirm extra payments are actually allowed without a fee before planning around them.<\/li>\n<li><b>Ignoring refinance closing costs<\/b> \u2014 a lower rate isn't automatically worth it if the break-even point exceeds how long you'll keep the loan.<\/li>\n<li><b>Confusing APR with the interest rate<\/b> \u2014 APR includes certain fees and gives a more complete cost comparison between offers.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/loan\">Loan Calculator<\/a> for a broader loan-planning view, the <a href=\"#\/calculator\/mortgage\">Mortgage Calculator<\/a> for home-specific features, the <a href=\"#\/calculator\/auto-loan\">Auto Loan Calculator<\/a> for vehicle financing, and the <a href=\"#\/calculator\/compound-interest\">Compound Interest Calculator<\/a> for related interest math.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is an amortization schedule?<\/summary><p>A complete list of every loan payment over its term, showing how much of each goes to interest vs. principal, and the resulting balance after each payment.<\/p><\/details>\n<details><summary>How is amortization calculated?<\/summary><p>Each period's interest is the balance times the periodic rate; the rest of the fixed payment reduces principal, and the new balance carries forward to the next period.<\/p><\/details>\n<details><summary>Why are early payments mostly interest?<\/summary><p>Because interest is calculated on the outstanding balance, which is largest early in the loan \u2014 the interest portion naturally declines as the balance shrinks.<\/p><\/details>\n<details><summary>How do extra payments help?<\/summary><p>They reduce principal directly, lowering the interest calculated on every remaining period \u2014 see the extra-payment simulator above for your exact savings.<\/p><\/details>\n<details><summary>Can I pay off my loan early?<\/summary><p>Usually yes, though check your loan agreement for any prepayment penalty first.<\/p><\/details>\n<details><summary>What is a balloon payment?<\/summary><p>A large lump-sum payment due at the end of certain loan structures, after smaller regular payments throughout the term.<\/p><\/details>\n<details><summary>What is APR?<\/summary><p>Annual Percentage Rate \u2014 the interest rate plus certain fees, expressed as one yearly percentage for a more complete cost comparison.<\/p><\/details>\n<details><summary>Does payment frequency matter?<\/summary><p>It has a modest effect on total interest \u2014 the frequency comparison table above shows this directly, though rate and term matter far more.<\/p><\/details>\n<details><summary>How do bi-weekly payments reduce interest?<\/summary><p>They reduce the balance slightly more often than monthly payments, marginally lowering the interest that accrues over the loan's life.<\/p><\/details>\n<details><summary>Can I download my amortization schedule?<\/summary><p>Yes \u2014 use the CSV download button above to export the full payment-by-payment schedule.<\/p><\/details>\n<details><summary>How is interest calculated each period?<\/summary><p>Outstanding balance multiplied by the periodic interest rate (annual rate divided by payments per year).<\/p><\/details>\n<details><summary>Should I refinance?<\/summary><p>Depends on your new rate, remaining term, and closing costs \u2014 the refinance analysis above calculates your specific savings and break-even point.<\/p><\/details>\n<details><summary>What is negative amortization?<\/summary><p>When a payment is smaller than the period's accrued interest, causing the balance to grow instead of shrink \u2014 uncommon in standard fixed-rate loans.<\/p><\/details>\n<details><summary>What happens if I miss a payment?<\/summary><p>Typically late fees and continued interest accrual on the outstanding balance, plus potential credit impact \u2014 specifics vary by lender.<\/p><\/details>\n<details><summary>What is the difference between simple and amortized loans?<\/summary><p>Simple interest loans calculate interest only on the original principal for the full term; amortized loans recalculate interest each period on the declining balance, which is how most mortgages and auto loans actually work.<\/p><\/details>\n<details><summary>How does a shorter loan term affect total interest?<\/summary><p>It reduces total interest substantially, though it increases the payment amount \u2014 see the term comparison table above for the specific trade-off on your loan.<\/p><\/details>\n<details><summary>What fees should I include?<\/summary><p>Origination fees, closing costs, and any other loan-related charges \u2014 this calculator's refinance section includes a dedicated closing-costs field.<\/p><\/details>\n<details><summary>Can I change the start date?<\/summary><p>This calculator focuses on the payment-by-payment schedule; add your own start date externally to map period numbers to actual calendar dates if needed.<\/p><\/details>\n<details><summary>How do taxes and insurance affect payments?<\/summary><p>For loans like mortgages, taxes and insurance are often bundled into the total monthly payment (escrow) \u2014 this calculator focuses on principal and interest; see the Mortgage Calculator for a version that includes these additional costs.<\/p><\/details>\n<details><summary>Is amortization used for all loans?<\/summary><p>Most consumer installment loans (mortgages, auto loans, personal loans) are amortizing, though some loan types use simple interest or interest-only structures instead.<\/p><\/details>\n<details><summary>What is the difference between amortization and depreciation?<\/summary><p>Amortization describes paying down a loan balance over time; depreciation describes an asset losing value over time \u2014 related concepts in accounting, but applied to different things.<\/p><\/details>\n<details><summary>How much of my payment is interest in year 1 vs. year 10?<\/summary><p>Substantially more in year 1 \u2014 check the annual summary table above to see the exact interest\/principal split for any year of your specific loan.<\/p><\/details>\n<details><summary>Can I see every single payment, not just a summary?<\/summary><p>Yes \u2014 switch to \"Every payment\" view above for the complete payment-by-payment schedule, searchable by payment number.<\/p><\/details>\n<details><summary>What's a good interest rate for a loan?<\/summary><p>This varies significantly by loan type, credit profile, and market conditions \u2014 the rate comparison table above shows how sensitive your specific payment and total interest are to rate changes.<\/p><\/details>\n<details><summary>How long does it take to pay off a loan?<\/summary><p>Determined by your term, though extra payments can meaningfully shorten it \u2014 see the extra-payment simulator above for your specific payoff acceleration.<\/p><\/details>\n<details><summary>Does making extra payments reduce my required monthly payment?<\/summary><p>Typically no \u2014 the required minimum payment usually stays the same; extra payments instead shorten the loan's remaining term and reduce total interest.<\/p><\/details>\n<details><summary>What's the break-even point for refinancing?<\/summary><p>The point where cumulative monthly savings from the new loan equal the closing costs paid to refinance \u2014 calculated directly in the refinance analysis above.<\/p><\/details>\n<details><summary>Is a 15-year or 30-year loan better?<\/summary><p>A 15-year loan has a higher payment but substantially less total interest; a 30-year loan is more affordable monthly but costs more overall \u2014 compare both directly in the term comparison table above.<\/p><\/details>\n<details><summary>Can extra payments eliminate a prepayment penalty risk?<\/summary><p>No \u2014 a prepayment penalty (if your loan has one) applies regardless of the calculator; always check your specific loan agreement before making large extra payments.<\/p><\/details>\n<details><summary>How accurate is this calculator compared to my lender's numbers?<\/summary><p>It uses the same standard amortization formula lenders use for fixed-rate loans \u2014 actual statements may include additional fees or rounding conventions specific to your lender, so treat results as a close, reliable estimate.<\/p><\/details>\n<details><summary>Why does my remaining balance look higher than expected after several years?<\/summary><p>This is normal for long-term loans (like 30-year mortgages) \u2014 because early payments are interest-heavy, the balance declines slowly at first and much faster in later years.<\/p><\/details>\n<details><summary>Can I compare multiple interest rates at once?<\/summary><p>Yes \u2014 the interest rate comparison table above shows your payment and total interest across a range of rates around your entered rate.<\/p><\/details>\n<details><summary>Does the payoff date change with extra payments?<\/summary><p>Yes \u2014 the extra-payment simulator shows exactly how much time is saved off your payoff date for any extra amount you enter.<\/p><\/details>\n<details><summary>What's the difference between an amortization calculator and a loan calculator?<\/summary><p>They share the same underlying math; this calculator emphasizes the full detailed schedule, refinancing, and frequency\/term comparisons, while a general loan calculator often focuses more on the upfront payment estimate.<\/p><\/details>`,\n\n'sales-tax':`\n<h2>Sales tax calculator \u2014 a complete shopping and business toolkit<\/h2>\n<p>This calculator covers single items with discounts and shipping, a multi-item cart for real shopping trips, a tax-rate finder that works backward from a receipt total, and a shopping budget planner that tells you the maximum sticker price you can afford before tax pushes you over budget.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Adding tax<\/div>\n  <div class=\"f-eq\">Tax = Price \u00d7 Rate &nbsp;|&nbsp; Total = Price + Tax<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Removing tax (finding the pre-tax price)<\/div>\n  <div class=\"f-eq\">Pre-tax price = Total \u00f7 (1 + Rate)<\/div>\n<\/div>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">An $80 item at an 8.25% sales tax rate adds $6.60 in tax, for a total of <b>$86.60<\/b>.<\/p>\n<\/div>\n\n<h2>What is sales tax?<\/h2>\n<p>Sales tax is a consumption tax added to the price of most goods and services at the point of purchase, collected by the seller and remitted to the relevant government. Unlike income tax, it's generally calculated as a flat percentage of the purchase price, regardless of the buyer's income.<\/p>\n\n<h2>How sales tax is calculated<\/h2>\n<p>The core calculation is simple \u2014 multiply the taxable price by the tax rate \u2014 but the \"taxable price\" itself can be more nuanced once discounts, coupons, and shipping enter the picture, which is exactly why this calculator breaks each of those out separately in the itemized receipt above.<\/p>\n\n<h2>Sales tax vs. VAT vs. GST<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>System<\/th><th>How it works<\/th><th>Common in<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Sales tax<\/td><td>Charged once, at the final point of sale to the end consumer<\/td><td>United States (state\/local level)<\/td><\/tr>\n    <tr><td>VAT (Value Added Tax)<\/td><td>Charged at each stage of production\/distribution, with businesses reclaiming tax paid on inputs<\/td><td>European Union, UK, and many other countries<\/td><\/tr>\n    <tr><td>GST (Goods and Services Tax)<\/td><td>Similar in structure to VAT \u2014 a multi-stage consumption tax<\/td><td>Canada, Australia, India, and others<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>The end consumer's out-of-pocket cost under all three systems is often conceptually similar, but the underlying mechanics \u2014 especially who can reclaim tax paid along the supply chain \u2014 differ significantly. If you need VAT or GST-specific calculations, use a calculator built for that system's rules rather than a generic sales tax tool.<\/p>\n\n<h2>Are discounts applied before or after tax?<\/h2>\n<p>In most jurisdictions, discounts and coupons are applied to reduce the taxable amount <em>before<\/em> tax is calculated \u2014 meaning you pay tax on the discounted price, not the original sticker price. The \"discount timing comparison\" above shows exactly how much this ordering saves compared to taxing the full price first, though the specific rule can vary by jurisdiction and discount type (manufacturer coupons are sometimes treated differently than store discounts).<\/p>\n\n<h2>Is shipping taxable?<\/h2>\n<p>This varies by jurisdiction \u2014 some tax authorities include shipping and handling charges as part of the taxable sale, while others exempt them, particularly when shipping is separately stated on the invoice. This calculator treats shipping as non-taxable by default (a common convention), but always verify your specific jurisdiction's rule for precise compliance needs.<\/p>\n\n<h2>Tax-exempt purchases<\/h2>\n<p>Many jurisdictions exempt certain categories from sales tax \u2014 common examples include groceries, prescription medications, and certain clothing items, though exact exemptions vary enormously by location. Nonprofit and resale purchases (with proper documentation) are also frequently exempt.<\/p>\n\n<h2>State and local sales tax in the US<\/h2>\n<p>In the United States, sales tax is set at the state level, and many states allow additional local (county or city) tax on top \u2014 meaning the effective combined rate can vary block by block in some areas. There's also no federal sales tax in the US, unlike VAT\/GST countries where the tax is typically set nationally (sometimes with regional variation on top).<\/p>\n\n<h2>Online shopping and sales tax<\/h2>\n<p>Following various legal developments, most online retailers in the US now collect sales tax based on the buyer's shipping destination, even if the retailer has no physical presence in that state \u2014 a significant shift from earlier practice. Rules continue to evolve, so specific obligations can vary by seller size and state.<\/p>\n\n<h2>Business sales tax basics<\/h2>\n<p>Businesses generally collect sales tax from customers and remit it to the relevant tax authority on a periodic (often monthly or quarterly) basis. In VAT\/GST systems, registered businesses can typically reclaim the tax they paid on business inputs \u2014 a mechanism that doesn't exist in the traditional single-stage US sales tax model.<\/p>\n\n<h2>Common mistakes<\/h2>\n<ul>\n<li><b>Taxing the discounted price incorrectly<\/b> \u2014 confirm whether your jurisdiction taxes the pre- or post-discount amount.<\/li>\n<li><b>Forgetting combined state + local rates<\/b> \u2014 using only the state rate can understate the actual tax due in areas with local add-ons.<\/li>\n<li><b>Assuming shipping is always tax-exempt<\/b> \u2014 this varies by location and how the shipping charge is presented on the invoice.<\/li>\n<li><b>Confusing sales tax with VAT\/GST<\/b> \u2014 these are structurally different systems; don't use a sales tax tool for VAT-inclusive pricing without adjusting your approach.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/discount\">Discount Calculator<\/a> for markdown-focused math, the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for general percentage problems, and the <a href=\"#\/calculator\/tip\">Tip Calculator<\/a> for restaurant bills.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is sales tax?<\/summary><p>A consumption tax added to the price of most goods and services at the point of sale, calculated as a percentage of the purchase price.<\/p><\/details>\n<details><summary>How do I calculate sales tax?<\/summary><p>Multiply the taxable price by the tax rate \u2014 for example, $80 \u00d7 8.25% = $6.60 in tax.<\/p><\/details>\n<details><summary>How do I remove sales tax from a price?<\/summary><p>Divide the tax-included total by (1 + tax rate) \u2014 use the \"Price including tax\" mode above to do this instantly.<\/p><\/details>\n<details><summary>How is sales tax different from VAT?<\/summary><p>Sales tax is charged once at the final sale to the consumer; VAT is charged at each stage of production and distribution, with registered businesses able to reclaim VAT paid on their own inputs.<\/p><\/details>\n<details><summary>What is GST?<\/summary><p>Goods and Services Tax \u2014 structurally similar to VAT, used in countries like Canada, Australia, and India.<\/p><\/details>\n<details><summary>Is shipping taxable?<\/summary><p>It depends on the jurisdiction \u2014 some tax shipping and handling, others don't, particularly when it's separately itemized on the invoice.<\/p><\/details>\n<details><summary>Are discounts applied before or after tax?<\/summary><p>Typically before \u2014 you generally pay tax on the discounted price, not the original price, though specific rules vary by jurisdiction and discount type.<\/p><\/details>\n<details><summary>What items are tax exempt?<\/summary><p>Common exemptions include groceries, prescription medication, and certain clothing in many jurisdictions \u2014 exact rules vary significantly by location.<\/p><\/details>\n<details><summary>Why do states have different tax rates?<\/summary><p>In the US, sales tax is set at the state level (with many states allowing additional local tax), so rates naturally vary by location rather than being set at a national level.<\/p><\/details>\n<details><summary>Can businesses reclaim sales tax?<\/summary><p>Under a traditional US-style sales tax, generally no \u2014 reclaiming input tax is a defining feature of VAT\/GST systems instead.<\/p><\/details>\n<details><summary>How do I calculate tax for multiple items?<\/summary><p>Use the multi-item cart mode above \u2014 add each item's price and quantity, and it computes the combined subtotal, tax, and grand total.<\/p><\/details>\n<details><summary>How do I calculate tax from the final price?<\/summary><p>Switch to \"Price including tax\" mode above, or use the tax-rate finder if you also know the original pre-tax price.<\/p><\/details>\n<details><summary>Is online shopping taxed?<\/summary><p>Generally yes in most US states today, typically based on the buyer's shipping address, following various legal changes in recent years.<\/p><\/details>\n<details><summary>Does every country use sales tax?<\/summary><p>No \u2014 many countries use VAT or GST instead, which work differently from a traditional single-stage sales tax.<\/p><\/details>\n<details><summary>How often do tax rates change?<\/summary><p>Rates can change periodically at the state, county, or city level \u2014 always verify the current rate for your specific location rather than assuming it's fixed indefinitely.<\/p><\/details>\n<details><summary>Can I print my calculation?<\/summary><p>Yes \u2014 use your browser's print function on this page, or copy the result using the copy button above.<\/p><\/details>\n<details><summary>How do I find the tax rate from a receipt?<\/summary><p>Use the \"Find tax rate\" mode above \u2014 enter the original price and the final price, and it calculates the implied rate.<\/p><\/details>\n<details><summary>What's the maximum I can spend before tax to stay within budget?<\/summary><p>Use the \"Shopping budget\" mode above \u2014 enter your total budget and tax rate to find the maximum pre-tax sticker price.<\/p><\/details>\n<details><summary>Do all US states have sales tax?<\/summary><p>No \u2014 a handful of US states have no statewide sales tax, though local exceptions and other tax structures can still apply; check your specific state's current rules.<\/p><\/details>\n<details><summary>Is sales tax the same on every product?<\/summary><p>Not always \u2014 some categories (like groceries or clothing) may have reduced rates or exemptions depending on the jurisdiction.<\/p><\/details>\n<details><summary>How does combined state and local tax work?<\/summary><p>The local rate is typically added on top of the state rate to form the total effective rate charged at that specific location.<\/p><\/details>\n<details><summary>What's the difference between tax-inclusive and tax-exclusive pricing?<\/summary><p>Tax-exclusive pricing shows the price before tax is added; tax-inclusive pricing already has tax built into the displayed price \u2014 the two \"Single item\" modes above handle both directions.<\/p><\/details>\n<details><summary>Can I calculate tax on a wholesale purchase?<\/summary><p>Yes, the same formula applies \u2014 though wholesale and resale transactions are sometimes exempt from sales tax with proper documentation, depending on jurisdiction.<\/p><\/details>\n<details><summary>How accurate is this calculator for my specific state?<\/summary><p>The math is exact for whatever rate you enter \u2014 accuracy for your specific location depends on entering the correct current combined state and local rate.<\/p><\/details>\n<details><summary>What happens if I enter the wrong tax rate?<\/summary><p>The calculation will be mathematically correct for the rate entered, but won't reflect your actual location's tax \u2014 always verify your local combined rate for precise results.<\/p><\/details>\n<details><summary>Can this calculator handle multiple tax rates on one order?<\/summary><p>The current cart mode applies one shared rate to all items \u2014 for orders with genuinely mixed tax categories, calculate exempt and taxable items separately.<\/p><\/details>\n<details><summary>Is service tax the same as sales tax?<\/summary><p>Not necessarily \u2014 some jurisdictions tax services differently from goods, or not at all; check your local rules for services specifically.<\/p><\/details>\n<details><summary>How do coupons affect sales tax differently from store discounts?<\/summary><p>In some jurisdictions, manufacturer coupons (reimbursed to the retailer by a third party) are taxed differently than store-funded discounts \u2014 check your local rules if this distinction matters for your calculation.<\/p><\/details>`,\n\ntip:`\n<h2>Tip calculator \u2014 a complete dining and tipping assistant<\/h2>\n<p>This calculator instantly computes tips and equal splits, handles unequal bill splitting (so people who ordered less don't subsidize people who ordered more), works in reverse from a receipt total, and includes a general tipping-customs reference for different countries and service types.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Formula<\/div>\n  <div class=\"f-eq\">Tip = Bill \u00d7 Tip% &nbsp;|&nbsp; Per person = (Bill + Tip) \u00f7 People<\/div>\n<\/div>\n\n<h2>What is a tip?<\/h2>\n<p>A tip (or gratuity) is a voluntary payment given in addition to the base price of a service, traditionally as a way to reward or reward good service. Whether tipping is expected, optional, or uncommon varies enormously by country, service type, and even specific establishment.<\/p>\n\n<h2>How to calculate a tip quickly<\/h2>\n<p>A simple mental shortcut for a 20% tip: move the decimal point on the bill one place left (10%), then double it. For 15%, take the 10% figure and add half of it again. This calculator does the exact math instantly, but these shortcuts are handy when you don't have your phone out.<\/p>\n\n<h2>Typical tipping percentages<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Service level<\/th><th>Commonly cited range<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Below average<\/td><td>10-12%<\/td><\/tr>\n    <tr><td>Average<\/td><td>15%<\/td><\/tr>\n    <tr><td>Good<\/td><td>18-20%<\/td><\/tr>\n    <tr><td>Excellent<\/td><td>20-25%<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>These are general, commonly cited ranges rather than fixed rules \u2014 actual norms vary by region, restaurant type, and personal circumstances.<\/p>\n\n<h2>Should I tip before or after tax?<\/h2>\n<p>Many people tip based on the pre-tax bill amount, since tax isn't part of the cost of the service itself \u2014 this is this calculator's default. Others prefer tipping on the post-tax total for simplicity. Neither is universally \"correct\"; the tax-before\/after toggle above lets you calculate either way and see the difference directly.<\/p>\n\n<h2>Service charge vs. tip<\/h2>\n<p>A <b>service charge<\/b> is a mandatory fee added to the bill by the establishment (common for large groups, or standard practice in some countries); a <b>tip<\/b> is a voluntary amount left at the customer's discretion. If a service charge is already included, check whether additional tipping is expected or unusual before adding more \u2014 practices vary by location.<\/p>\n\n<h2>Splitting bills fairly<\/h2>\n<p>An equal split (dividing the total evenly) is simplest, but can feel unfair when people ordered very differently. The unequal-split mode above divides tax and tip proportionally to what each person actually spent \u2014 someone who ordered a $55 entr\u00e9e pays a larger share of the tax and tip than someone who ordered a $20 salad, which many people consider the fairer approach for mixed-order groups.<\/p>\n\n<h2>Tipping etiquette in different countries<\/h2>\n<p>Tipping customs vary dramatically worldwide \u2014 from expected practices in the United States to countries where tipping is uncommon or can even feel unusual to service staff. See the tipping guide tab above for general, non-prescriptive notes by country \u2014 always treat these as starting points, not fixed rules, since local and situational variation is common.<\/p>\n\n<h2>Digital payment tipping<\/h2>\n<p>Many point-of-sale systems now prompt for a tip percentage directly on the payment screen, sometimes with pre-set (and occasionally higher-than-traditional) suggested percentages. There's no obligation to select a prompted amount \u2014 calculating your own preferred tip, as this calculator does, is always a reasonable alternative to whatever a screen suggests.<\/p>\n\n<h2>Common tipping mistakes<\/h2>\n<ul>\n<li><b>Forgetting to check for an included service charge<\/b> \u2014 tipping again on top of an already-included charge means paying more than intended.<\/li>\n<li><b>Splitting unequal orders evenly<\/b> \u2014 can feel unfair to people who ordered less; use the unequal-split mode above when order sizes differ significantly.<\/li>\n<li><b>Not accounting for local customs when traveling<\/b> \u2014 tipping norms that are standard at home can be unusual (or, in some cases, insufficient) elsewhere.<\/li>\n<li><b>Confusing gratuity with sales tax<\/b> \u2014 these are separate line items serving completely different purposes.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/sales-tax\">Sales Tax Calculator<\/a> for tax-specific math, the <a href=\"#\/calculator\/discount\">Discount Calculator<\/a> for markdown pricing, and the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for general percentage problems.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I calculate a tip?<\/summary><p>Multiply the bill by your chosen tip percentage \u2014 for example, a $64 bill at 18% is $64 \u00d7 0.18 = $11.52.<\/p><\/details>\n<details><summary>How much should I tip?<\/summary><p>There's no universal rule \u2014 15-20% is a commonly cited range in the US for restaurant service, though it varies by region, service type, and personal judgment.<\/p><\/details>\n<details><summary>Should I tip before or after tax?<\/summary><p>Many people tip on the pre-tax amount, but both approaches are common \u2014 use the tax-before\/after toggle above to calculate either way.<\/p><\/details>\n<details><summary>How do I split a bill?<\/summary><p>Divide the bill plus tip by the number of people for an equal split, or use the unequal-split mode above to divide proportionally based on individual orders.<\/p><\/details>\n<details><summary>What is a service charge?<\/summary><p>A mandatory fee added to the bill by the establishment, distinct from a voluntary tip \u2014 check whether one is already included before adding an additional tip.<\/p><\/details>\n<details><summary>Is service charge the same as a tip?<\/summary><p>No \u2014 a service charge is mandatory and set by the business; a tip is voluntary and set by the customer.<\/p><\/details>\n<details><summary>What is the standard restaurant tip?<\/summary><p>Commonly cited as 15-20% in the US for typical table service, though this varies by region and isn't a strict rule.<\/p><\/details>\n<details><summary>Should I tip for takeout?<\/summary><p>Practices vary \u2014 some people tip a smaller percentage for takeout than for full table service; there's no universal standard.<\/p><\/details>\n<details><summary>Should I tip delivery drivers?<\/summary><p>Commonly yes in many regions, often varying with distance, weather, and service quality \u2014 check your delivery platform's guidance if unsure.<\/p><\/details>\n<details><summary>Should I tip hotel staff?<\/summary><p>Many people tip housekeeping a small amount per night and bellhops per bag \u2014 practices and amounts vary by region and hotel type.<\/p><\/details>\n<details><summary>How do I round the tip?<\/summary><p>Use the rounding option above \u2014 round the tip itself up to the nearest dollar, or round the entire total up instead.<\/p><\/details>\n<details><summary>How do I calculate a 20% tip quickly?<\/summary><p>Move the decimal point one place left on the bill (that's 10%), then double the result.<\/p><\/details>\n<details><summary>Is tipping mandatory?<\/summary><p>Generally no \u2014 it's voluntary in most places, though social expectation can vary significantly by country and service type, and some establishments add a mandatory service charge instead.<\/p><\/details>\n<details><summary>Which countries do not expect tips?<\/summary><p>Several countries have limited or no tipping culture \u2014 Japan is a commonly cited example \u2014 though practices continue to evolve; see the tipping guide above for general notes.<\/p><\/details>\n<details><summary>Can I calculate a tip on multiple bills?<\/summary><p>Yes \u2014 use the unequal-split mode above to enter multiple individual amounts and calculate everyone's proportional share.<\/p><\/details>\n<details><summary>What's the fairest way to split a group bill?<\/summary><p>Proportional splitting (based on what each person actually ordered) is generally considered fairer than an equal split when order amounts differ significantly \u2014 see the unequal-split mode above.<\/p><\/details>\n<details><summary>How do I find the tip percentage from a receipt?<\/summary><p>Use the reverse-tip mode above \u2014 enter the original bill and the total you paid, and it calculates the implied tip percentage.<\/p><\/details>\n<details><summary>Does the tip calculator work for large groups?<\/summary><p>Yes \u2014 both the equal-split and unequal-split modes scale to any number of people.<\/p><\/details>\n<details><summary>What's a good tip for excellent service?<\/summary><p>Often cited in the 20-25% range, though this is a general guideline, not a fixed requirement.<\/p><\/details>\n<details><summary>Do I tip on the full bill or just my portion?<\/summary><p>Depends on the group's arrangement \u2014 the unequal-split mode above lets each person's tip be based on what they specifically ordered.<\/p><\/details>\n<details><summary>Is it rude not to tip?<\/summary><p>Social expectations vary significantly by country and context \u2014 in some places it's a strong social norm, in others it's optional or uncommon; use the tipping guide above for general orientation.<\/p><\/details>\n<details><summary>How does digital payment tipping work?<\/summary><p>Many point-of-sale systems prompt a suggested tip percentage at checkout \u2014 you're generally not obligated to select a prompted amount and can always calculate your own preferred tip instead.<\/p><\/details>\n<details><summary>Should I tip on delivery fees?<\/summary><p>Practices vary by platform and region \u2014 many people tip based on the food total rather than delivery\/service fees, but check your specific delivery service's guidance.<\/p><\/details>\n<details><summary>What if the service was poor?<\/summary><p>Practices vary \u2014 some people still tip a smaller amount, others tip nothing; this is a personal judgment call that also depends on local norms and the specific situation.<\/p><\/details>\n<details><summary>Can I use this calculator for bars?<\/summary><p>Yes \u2014 the same tip and split logic works for bar tabs, though bartender tipping customs sometimes differ slightly from full-service restaurant norms (e.g., a per-drink amount instead of a percentage).<\/p><\/details>\n<details><summary>How do tipping customs differ for salons and personal services?<\/summary><p>Often a percentage of the service cost, similar to dining, though specific customs vary \u2014 see the \"tipping by service type\" table above for general notes.<\/p><\/details>\n<details><summary>What's the difference between gratuity and a tip?<\/summary><p>They're generally used interchangeably to mean the same voluntary payment for service.<\/p><\/details>\n<details><summary>Can I calculate a tip in a different currency?<\/summary><p>Yes \u2014 enter your bill amount in whatever currency you're working with; the percentage math is currency-independent.<\/p><\/details>\n<details><summary>Does this calculator save my tip history?<\/summary><p>No \u2014 calculations happen locally in your browser and aren't stored or saved between visits.<\/p><\/details>\n<details><summary>Why does my per-person total look different in unequal split vs. equal split?<\/summary><p>Because unequal split allocates tax and tip proportionally to each person's actual order, while equal split simply divides the whole bill evenly \u2014 they'll only match if everyone ordered the same amount.<\/p><\/details>`,\n\ndiscount:`\n<h2>Discount calculator \u2014 a complete shopping savings toolkit<\/h2>\n<p>This calculator covers single-item discounts with coupons, tax, shipping and cashback layered on top, unlimited sequential discount stacking, reverse calculators (find the discount percentage or the original price), a 3-store deal comparison, and bulk purchase math for business buyers.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Core formulas<\/div>\n  <div class=\"f-eq\">Sale price = Price \u00d7 (1 \u2212 Discount%) &nbsp;|&nbsp; Savings = Price \u00d7 Discount%<\/div>\n<\/div>\n<div class=\"example-box\">\n  <div class=\"e-label\">Worked example<\/div>\n  <p style=\"margin:0;\">A $120 item at 25% off saves $30, bringing the final price to <b>$90<\/b>.<\/p>\n<\/div>\n\n<h2>What is a discount?<\/h2>\n<p>A discount is a reduction from a stated or original price, expressed either as a percentage (like \"25% off\") or a fixed amount (like \"$20 off\"). Both approaches are common, and which one saves more money depends entirely on the original price \u2014 a fixed $20 off is a much bigger relative discount on a $40 item than on a $400 item.<\/p>\n\n<h2>Percentage discounts vs. fixed amount discounts<\/h2>\n<p>A percentage discount scales with price \u2014 25% off is always 25% off, whether the item costs $10 or $1,000. A fixed discount stays the same dollar amount regardless of price. This means \"which is better, 20% off or $20 off\" depends entirely on the item's price: on a $100 item, both are equivalent; below $100, the fixed $20 saves more; above $100, the 20% saves more.<\/p>\n\n<h2>Coupons vs. discounts<\/h2>\n<p>A \"discount\" often refers to the retailer's own price reduction (a sale), while a \"coupon\" is typically a separate, additional reduction \u2014 sometimes issued by the store, sometimes by the manufacturer \u2014 that's applied on top of (or instead of) the sale price. This calculator's coupon field applies after the main discount, matching how most checkout flows stack these two.<\/p>\n\n<h2>How stacked (sequential) discounts actually work<\/h2>\n<p>This is one of the most misunderstood parts of shopping math: sequential discounts <b>multiply<\/b>, they don't add. A 30% discount followed by a 10% discount does <em>not<\/em> equal 40% off \u2014 it equals roughly 37% off, because the second discount applies to the already-reduced price, not the original. The multiple-discounts mode above shows this explicitly, including the \"equivalent single discount\" figure so you can see exactly how much stacking actually saved.<\/p>\n\n<h2>Why doesn't 50% + 50% equal 100% off?<\/h2>\n<p>Applying 50% off twice leaves the item at 25% of its original price (a 75% total discount), not free \u2014 because the second 50% is calculated on the already-halved price, not the original. This is the same multiplicative principle behind all sequential discounts, just at an intuitive extreme that makes the effect obvious.<\/p>\n\n<h2>Is tax calculated before or after a discount?<\/h2>\n<p>In most jurisdictions, sales tax is calculated on the discounted price, not the original price \u2014 meaning tax is applied after the discount. This calculator defaults to that convention but includes a toggle to calculate tax on the pre-discount price instead, since rules and receipt presentation can vary by jurisdiction and specific promotion type.<\/p>\n\n<h2>What is cashback?<\/h2>\n<p>Cashback is money returned to the buyer after a purchase is completed \u2014 often through a credit card rewards program, browser extension, or retailer promotion \u2014 effectively lowering the item's true cost below the checkout total. Unlike a discount or coupon (which reduces the price you pay at checkout), cashback typically arrives afterward, which is why this calculator shows both your \"checkout total\" and your separate \"effective price\" once cashback is factored in.<\/p>\n\n<h2>Coupon vs. rebate<\/h2>\n<p>A coupon typically reduces the price at the point of sale; a rebate is usually a partial refund claimed <em>after<\/em> purchase, often requiring proof of purchase or a separate submission process. Both reduce your effective cost, but on different timelines \u2014 a rebate behaves more like the cashback modeled above than like an at-checkout coupon.<\/p>\n\n<h2>BOGO (Buy One, Get One) offers<\/h2>\n<p>A \"Buy One Get One Free\" offer is mathematically equivalent to a 50% discount across two items of equal price \u2014 you pay full price for one and nothing for the second, averaging to half off both. Variations like \"Buy One Get One 50% Off\" work out to a 25% average discount across the pair. When comparing a BOGO deal against a straightforward percentage-off sale, it often helps to convert both into an equivalent blended discount percentage for a fair comparison.<\/p>\n\n<h2>How to compare deals effectively<\/h2>\n<p>The single biggest mistake in comparing sales is looking only at the advertised discount percentage rather than the final checkout price. A bigger percentage off a higher list price, plus shipping, can easily cost more than a smaller percentage off a lower list price with free shipping \u2014 the 3-store comparison tool above is built specifically to surface this, since it factors in discount, shipping, and price together rather than any single number in isolation.<\/p>\n\n<h2>Common retail pricing tricks to understand<\/h2>\n<ul>\n<li><b>Inflated \"original\" prices<\/b> \u2014 a discount looks bigger if the reference price was inflated first; compare against genuinely typical prices when possible.<\/li>\n<li><b>Free shipping thresholds<\/b> \u2014 sometimes structured to encourage spending more than intended to unlock \"free\" shipping that costs more than the shipping fee itself would have.<\/li>\n<li><b>Percentage off large numbers looking more impressive than they are<\/b> \u2014 \"Save up to 70%\" often applies to only a small subset of inventory.<\/li>\n<li><b>Multiple stacked \"savings\" that look additive but aren't<\/b> \u2014 as covered above, sequential discounts multiply, and marketing sometimes presents them in a way that implies simple addition.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/sales-tax\">Sales Tax Calculator<\/a> for tax-focused math, the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for general percentage problems, and the <a href=\"#\/calculator\/tip\">Tip Calculator<\/a> for restaurant bills.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I calculate a discount?<\/summary><p>Multiply the original price by the discount percentage to find the savings, then subtract from the original price \u2014 or use the calculator above for an instant result with a full breakdown.<\/p><\/details>\n<details><summary>How do I calculate the original price?<\/summary><p>Divide the sale price by (1 \u2212 discount%) \u2014 use the \"Original price\" reverse-calculator mode above.<\/p><\/details>\n<details><summary>How do I calculate discount percentage?<\/summary><p>Subtract the sale price from the original price, divide by the original price, and multiply by 100 \u2014 or use the \"Discount %\" reverse-calculator mode above.<\/p><\/details>\n<details><summary>Can I apply multiple discounts?<\/summary><p>Yes \u2014 use the multiple-discounts mode above to stack as many sequential percentages as you need.<\/p><\/details>\n<details><summary>Do percentages add together?<\/summary><p>No \u2014 sequential discounts multiply, not add; 20% off then 10% off equals 28% total savings, not 30%.<\/p><\/details>\n<details><summary>How do coupons work?<\/summary><p>They typically apply an additional reduction on top of (or instead of) a sale discount, usually calculated on the already-discounted price at checkout.<\/p><\/details>\n<details><summary>Is tax calculated before or after discounts?<\/summary><p>Usually after \u2014 most jurisdictions tax the discounted price, though this calculator lets you toggle to before-discount tax calculation if your situation differs.<\/p><\/details>\n<details><summary>What is a markdown?<\/summary><p>Another term for a price reduction, commonly used in retail to describe permanently lowering a price rather than a temporary promotional discount.<\/p><\/details>\n<details><summary>What is cashback?<\/summary><p>Money returned to the buyer after a purchase, often through a card rewards program or promotion \u2014 it lowers your effective cost but arrives after checkout, unlike a coupon.<\/p><\/details>\n<details><summary>What is the difference between a coupon and a rebate?<\/summary><p>A coupon typically reduces the price at checkout; a rebate is usually a partial refund claimed after purchase, often requiring a separate submission.<\/p><\/details>\n<details><summary>Which is better: 20% off or $20 off?<\/summary><p>Depends on the price \u2014 on items under $100 the fixed $20 saves more, above $100 the 20% saves more, and at exactly $100 they're equal.<\/p><\/details>\n<details><summary>How do I compare two sale prices?<\/summary><p>Compare the actual final checkout cost (including shipping and tax), not just the advertised discount percentage \u2014 the deal comparison tool above is built for exactly this.<\/p><\/details>\n<details><summary>How do I calculate bulk discounts?<\/summary><p>Use the bulk purchase mode above \u2014 enter unit price, quantity, and discount to get total cost, total savings, and average cost per unit.<\/p><\/details>\n<details><summary>Can I print my calculation?<\/summary><p>Yes \u2014 use your browser's print function, or copy the result using the copy button above.<\/p><\/details>\n<details><summary>Why doesn't 50% + 50% equal 100%?<\/summary><p>Because the second discount applies to the already-halved price, not the original \u2014 two sequential 50% discounts leave the item at 25% of its original price, not free.<\/p><\/details>\n<details><summary>What is a stackable discount?<\/summary><p>A discount that can be combined with other discounts or coupons on the same purchase \u2014 always applied sequentially, multiplying rather than adding.<\/p><\/details>\n<details><summary>How do I calculate a BOGO offer?<\/summary><p>A standard \"Buy One Get One Free\" is equivalent to a 50% average discount across both items \u2014 model it as a single 50%-off calculation on the combined price of two items.<\/p><\/details>\n<details><summary>Is a bigger percentage discount always the better deal?<\/summary><p>Not necessarily \u2014 a bigger percentage off a higher list price, plus shipping or fees, can still cost more than a smaller percentage off a lower price; always compare final checkout totals.<\/p><\/details>\n<details><summary>How does shipping affect a \"good deal\"?<\/summary><p>Significantly \u2014 free shipping can make a smaller discount the better overall deal once you account for a competitor's paid shipping; the deal comparison tool above factors this in directly.<\/p><\/details>\n<details><summary>Can I calculate a discount with sales tax included?<\/summary><p>Yes \u2014 the single-item mode above includes a tax field with a toggle for whether tax applies before or after the discount.<\/p><\/details>\n<details><summary>What's the difference between a discount and a rebate?<\/summary><p>A discount reduces the price at checkout; a rebate is typically claimed and paid back after the purchase, often requiring documentation.<\/p><\/details>\n<details><summary>How do I calculate savings on a bulk order?<\/summary><p>Use the bulk purchase mode above \u2014 it computes total list price, total savings, tax, and the effective average cost per unit.<\/p><\/details>\n<details><summary>What does \"up to 70% off\" actually mean?<\/summary><p>It typically means the maximum discount available applies only to certain items \u2014 most items in such a sale are usually discounted by less than the headline figure.<\/p><\/details>\n<details><summary>Are online discount codes the same as in-store coupons?<\/summary><p>Functionally similar \u2014 both apply a price reduction at checkout, though online codes are often single-use and time-limited compared to some physical coupons.<\/p><\/details>\n<details><summary>Can I calculate multiple items with different discounts?<\/summary><p>For simple cases, calculate each item separately, or use the deal comparison mode to compare across up to three complete scenarios.<\/p><\/details>\n<details><summary>How does an \"original price\" comparison work if the original price was inflated?<\/summary><p>The calculator's math is accurate for whatever original price you enter \u2014 but if that reference price was artificially inflated by the retailer, the displayed \"savings\" will overstate the real value versus typical market pricing.<\/p><\/details>\n<details><summary>What is an effective discount percentage?<\/summary><p>The actual total percentage saved once every layer (discount, coupon, cashback) is accounted for \u2014 shown directly above as \"effective discount\" or \"equivalent single discount\" depending on the mode.<\/p><\/details>\n<details><summary>Should I apply a coupon before or after a sale discount?<\/summary><p>Most checkout systems apply coupons after the sale discount (on the already-reduced price) \u2014 this calculator follows that common convention by default.<\/p><\/details>\n<details><summary>Does the order of discounts matter mathematically?<\/summary><p>No \u2014 multiplying by (1\u221220%) then (1\u221210%) gives the same final result as the reverse order; only the total number of discount stages and their sizes matter, not the sequence.<\/p><\/details>\n<details><summary>How much do I really save with cashback?<\/summary><p>Your true effective cost is the checkout total minus the cashback amount \u2014 shown directly in the savings breakdown above when a cashback percentage is entered.<\/p><\/details>\n<details><summary>Can this calculator handle a fixed dollar discount instead of a percentage?<\/summary><p>Convert a fixed discount to an equivalent percentage first (dollar discount \u00f7 original price \u00d7 100), then enter that percentage \u2014 or simply subtract the fixed amount directly for a quick manual check.<\/p><\/details>\n<details><summary>What's the best way to evaluate a \"flash sale\"?<\/summary><p>Compare the final checkout price (including any shipping and tax) against your item's typical price elsewhere, not just against the flash sale's own advertised \"original\" price.<\/p><\/details>\n<details><summary>Do discount percentages ever go above 100%?<\/summary><p>No \u2014 a valid discount percentage is always between 0% and 100%; anything at or above 100% would imply the item is free or the retailer is paying you, which isn't how standard discounts work.<\/p><\/details>\n<details><summary>How accurate is the multi-discount calculator for real checkout systems?<\/summary><p>It applies the same sequential multiplication most checkout systems use \u2014 actual totals may differ slightly if a specific retailer applies discounts in a non-standard order or with rounding at each stage.<\/p><\/details>`,\n\npercentage:`\n<h2>Percentage calculator \u2014 10 calculation modes in one hub<\/h2>\n<p>Percentages show up everywhere \u2014 school grades, shopping discounts, business margins, lab reports, salary raises. This calculator handles ten distinct percentage problems from one page: basic percentage questions, increase\/decrease, percentage difference, percentage error, discounts, markup, profit margin, and tax \u2014 each with instant calculation, a formula, and a full step-by-step solution.<\/p>\n\n<h2>What is a percentage?<\/h2>\n<p>A percentage is a way of expressing a number as a fraction of 100 \u2014 \"50%\" literally means \"50 out of 100,\" or 0.5 as a decimal. The word comes from the Latin \"per centum,\" meaning \"by the hundred.\" Percentages are useful precisely because they let you compare proportions across different scales \u2014 \"20% off\" means the same relative discount whether the item costs $10 or $10,000.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">The core formula<\/div>\n  <div class=\"f-eq\">Percentage = (Part \u00f7 Whole) \u00d7 100<\/div>\n<\/div>\n<p>Every mode on this page is a variation of this same relationship, just solving for a different piece \u2014 the part, the whole, or the percentage itself.<\/p>\n\n<h2>Percentage increase vs. percentage difference \u2014 a common mix-up<\/h2>\n<p><b>Percentage change<\/b> (increase or decrease) is directional and uses the original value as the base: going from 100 to 150 is a 50% increase, but going from 150 back to 100 is only a 33.3% decrease \u2014 same two numbers, different answer, because the base changed. <b>Percentage difference<\/b> is symmetric and uses the average of the two numbers as the base, making it order-independent \u2014 useful when comparing two values where neither is clearly the \"original\" (like comparing two lab measurements or two competitors' prices).<\/p>\n\n<h2>Percentage error \u2014 why it matters in science<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Percentage error<\/div>\n  <div class=\"f-eq\">% Error = |Experimental \u2212 Actual| \u00f7 Actual \u00d7 100<\/div>\n<\/div>\n<p>Percentage error measures how far an experimental or measured value deviates from a known or accepted \"actual\" value, expressed as a percentage. It's a staple of physics and chemistry lab reports, and unlike percentage difference, it's not symmetric \u2014 the \"actual\" value is always the denominator, since it represents the reference truth being measured against.<\/p>\n\n<h2>Discounts, markup, and profit margin \u2014 related but distinct<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Concept<\/th><th>Formula<\/th><th>Used by<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Discount<\/td><td>Final = Original \u00d7 (1 \u2212 Discount% \u00f7 100)<\/td><td>Shoppers, retailers<\/td><\/tr>\n    <tr><td>Markup<\/td><td>Price = Cost \u00d7 (1 + Markup% \u00f7 100)<\/td><td>Retailers pricing from cost<\/td><\/tr>\n    <tr><td>Profit margin<\/td><td>Margin = (Revenue \u2212 Cost) \u00f7 Revenue \u00d7 100<\/td><td>Business profitability analysis<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>The most common confusion is between markup and margin: a 50% markup on a $100 cost gives a $150 price, but that $50 profit is only a 33.3% margin on the $150 revenue \u2014 markup and margin are calculated on different bases (cost vs. revenue) and are never the same number except at 0%.<\/p>\n\n<h2>Common percentage mistakes<\/h2>\n<ul>\n<li><b>Confusing percentage points with percentage change<\/b> \u2014 going from 20% to 25% is a 5 percentage-point increase, but a 25% relative increase (5 \u00f7 20 \u00d7 100).<\/li>\n<li><b>Using the wrong base for percentage change<\/b> \u2014 always divide by the original (starting) value, not the new one.<\/li>\n<li><b>Mixing up markup and margin<\/b> \u2014 they use different denominators and are never numerically equal above 0%.<\/li>\n<li><b>Forgetting compounding<\/b> \u2014 a 50% decrease followed by a 50% increase does not return you to the original value (it results in a 25% net decrease), since each percentage is calculated on a different base.<\/li>\n<\/ul>\n\n<h2>Percentages in daily life<\/h2>\n<p>Beyond the classroom, percentages drive everyday decisions: comparing a 20% discount against a 15% discount plus a coupon, understanding a raise expressed as \"3% increase,\" reading nutrition labels (\"% Daily Value\"), interpreting interest rates, or checking whether a sale's \"50% off, then extra 20% off\" is actually 60% off total (it's not \u2014 it's 60% off calculated in two steps, not a flat 70%).<\/p>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/fraction\">Fraction Calculator<\/a> for fraction-percentage conversion, the <a href=\"#\/calculator\/grade\">Grade Calculator<\/a> and <a href=\"#\/calculator\/gpa\">GPA Calculator<\/a> for academic percentages, the <a href=\"#\/calculator\/discount\">Discount Calculator<\/a> and <a href=\"#\/calculator\/sales-tax\">Sales Tax Calculator<\/a> for shopping, and the <a href=\"#\/calculator\/average\">Average Calculator<\/a> for related statistics.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I calculate a percentage?<\/summary><p>Divide the part by the whole and multiply by 100 \u2014 for example, 40 out of 160 is (40 \u00f7 160) \u00d7 100 = 25%.<\/p><\/details>\n<details><summary>What is the difference between percentage increase and percentage difference?<\/summary><p>Percentage increase\/decrease is directional and uses the original value as the base; percentage difference is symmetric and uses the average of both values, giving the same answer regardless of order.<\/p><\/details>\n<details><summary>How do I calculate a discount?<\/summary><p>Multiply the original price by the discount percentage to find the savings, then subtract from the original price \u2014 or use the discount mode above for an instant result with steps shown.<\/p><\/details>\n<details><summary>How do I find the original price after a discount?<\/summary><p>Divide the discounted price by (1 \u2212 discount% \u00f7 100) \u2014 for example, a $80 price after a 20% discount means the original was $80 \u00f7 0.8 = $100.<\/p><\/details>\n<details><summary>How do I calculate exam percentages?<\/summary><p>Divide the score earned by the total possible points and multiply by 100 \u2014 the same formula as any basic percentage calculation, available in the \"What is X% of Y\" and related modes above.<\/p><\/details>\n<details><summary>How do I calculate percentage error?<\/summary><p>Subtract the actual value from your experimental value, take the absolute value, divide by the actual value, and multiply by 100 \u2014 see the percentage error mode above for a worked example.<\/p><\/details>`,\n\nfraction:`\n<h2>Fraction calculator \u2014 operations, conversions and comparison in one hub<\/h2>\n<p>This calculator does more than add two fractions. It supports mixed numbers, converts between fractions, decimals and percentages in either direction, compares two fractions side by side, and shows the full step-by-step solution \u2014 including finding the least common denominator \u2014 for every calculation.<\/p>\n\n<h2>Fraction basics<\/h2>\n<p>A fraction represents a part of a whole, written as one number over another: the <b>numerator<\/b> (top) shows how many parts you have, and the <b>denominator<\/b> (bottom) shows how many equal parts make up the whole. In 3\/4, the numerator 3 means \"three parts,\" and the denominator 4 means \"out of four equal parts total.\"<\/p>\n\n<h2>Types of fractions<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Type<\/th><th>Example<\/th><th>Description<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Proper fraction<\/td><td>3\/4<\/td><td>Numerator smaller than denominator (value less than 1)<\/td><\/tr>\n    <tr><td>Improper fraction<\/td><td>7\/4<\/td><td>Numerator equal to or larger than denominator (value 1 or more)<\/td><\/tr>\n    <tr><td>Mixed number<\/td><td>1 3\/4<\/td><td>A whole number plus a proper fraction \u2014 equivalent to an improper fraction<\/td><\/tr>\n    <tr><td>Unit fraction<\/td><td>1\/4<\/td><td>Numerator of exactly 1<\/td><\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>How fraction addition and subtraction work<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Addition \/ subtraction (common denominator required)<\/div>\n  <div class=\"f-eq\">a\/b \u00b1 c\/d = (a\u00d7d \u00b1 c\u00d7b) \/ (b\u00d7d), then simplified using the LCD<\/div>\n<\/div>\n<p>Unlike multiplication, you can't add or subtract fractions directly unless they share a denominator. This calculator finds the <b>least common denominator (LCD)<\/b> \u2014 the smallest number both denominators divide into evenly \u2014 converts both fractions to that denominator, then adds or subtracts the numerators. The step-by-step solution above shows every part of this process explicitly.<\/p>\n\n<h2>How fraction multiplication and division work<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Multiplication<\/div>\n  <div class=\"f-eq\">a\/b \u00d7 c\/d = (a\u00d7c) \/ (b\u00d7d)<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Division<\/div>\n  <div class=\"f-eq\">a\/b \u00f7 c\/d = a\/b \u00d7 d\/c (flip and multiply)<\/div>\n<\/div>\n<p>Multiplication is the simplest operation \u2014 multiply numerators together and denominators together, no common denominator needed. Division works by flipping the second fraction (finding its reciprocal) and multiplying instead \u2014 \"dividing by a fraction\" is the same as \"multiplying by its flip.\"<\/p>\n\n<h2>Simplifying fractions<\/h2>\n<p>A fraction is in simplest form when its numerator and denominator share no common factors other than 1. This calculator simplifies automatically by finding the <b>greatest common factor (GCF)<\/b> of the numerator and denominator and dividing both by it \u2014 for example, 36\/48 has a GCF of 12, simplifying to 3\/4.<\/p>\n\n<h2>Equivalent fractions<\/h2>\n<p>Equivalent fractions represent the same value written differently \u2014 2\/3, 4\/6, 6\/9, and 8\/12 are all equal, since multiplying (or dividing) both the numerator and denominator by the same number never changes a fraction's actual value. This is the same principle used to find a common denominator when adding or subtracting.<\/p>\n\n<h2>Fractions, decimals and percentages<\/h2>\n<p>Every fraction has an equivalent decimal (found by dividing numerator by denominator) and percentage (the decimal \u00d7 100). Some fractions produce exact, terminating decimals (like 3\/4 = 0.75); others produce repeating decimals (like 1\/3 = 0.333...), which occurs whenever the simplified denominator has a prime factor other than 2 or 5. Converting the other direction \u2014 decimal or percentage to fraction \u2014 works by placing the decimal digits over the appropriate power of 10, then simplifying.<\/p>\n\n<h2>Common fraction mistakes<\/h2>\n<ul>\n<li><b>Adding numerators and denominators directly<\/b> \u2014 1\/2 + 1\/3 is not 2\/5; fractions need a common denominator first.<\/li>\n<li><b>Forgetting to flip when dividing<\/b> \u2014 division requires multiplying by the reciprocal, not dividing straight across.<\/li>\n<li><b>Leaving answers unsimplified<\/b> \u2014 6\/8 and 3\/4 are the same value, but 3\/4 is the expected simplified form in most contexts.<\/li>\n<li><b>Mishandling mixed numbers<\/b> \u2014 converting to an improper fraction first (whole \u00d7 denominator + numerator) avoids errors when adding or subtracting mixed numbers directly.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for percentage-fraction conversions, the <a href=\"#\/calculator\/average\">Average Calculator<\/a> for related statistics, and the <a href=\"#\/calculator\/scientific\">Scientific Calculator<\/a> for broader expressions.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do you add fractions?<\/summary><p>Find a common denominator, convert both fractions to that denominator, then add the numerators \u2014 the step-by-step solution above walks through this for any two fractions you enter.<\/p><\/details>\n<details><summary>What is an improper fraction?<\/summary><p>A fraction where the numerator is equal to or greater than the denominator (like 7\/4), representing a value of 1 or more \u2014 equivalent to a mixed number.<\/p><\/details>\n<details><summary>How do you simplify fractions?<\/summary><p>Divide both the numerator and denominator by their greatest common factor (GCF) \u2014 this calculator does this automatically for every result.<\/p><\/details>\n<details><summary>How do you convert fractions to decimals?<\/summary><p>Divide the numerator by the denominator \u2014 3\/4 becomes 3 \u00f7 4 = 0.75.<\/p><\/details>\n<details><summary>What is a mixed number?<\/summary><p>A whole number combined with a proper fraction, like 1 3\/4 \u2014 mathematically equivalent to the improper fraction 7\/4.<\/p><\/details>\n<details><summary>What is the LCD?<\/summary><p>The Least Common Denominator \u2014 the smallest number that both fraction denominators divide into evenly, required for adding or subtracting fractions with different denominators.<\/p><\/details>`,\n\naverage:`\n<h2>Average calculator \u2014 a complete statistics hub<\/h2>\n<p>\"Average\" usually means the arithmetic mean, but it's actually one of several distinct ways to summarize a dataset \u2014 this calculator supports the arithmetic mean with a full statistics dashboard, weighted average, geometric mean, harmonic mean, and trimmed mean, each suited to different situations.<\/p>\n\n<h2>The arithmetic mean<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Formula<\/div>\n  <div class=\"f-eq\">Mean = (Sum of all values) \u00f7 (Count of values)<\/div>\n<\/div>\n<p>This is what most people mean by \"average\" \u2014 add everything up and divide by how many values there are. It's the right choice for most everyday situations, but it's sensitive to extreme values (outliers), which is exactly why this calculator also reports the median and flags potential outliers alongside the mean.<\/p>\n\n<h2>Mean vs. median \u2014 why they can disagree<\/h2>\n<p>The <b>median<\/b> is the middle value when data is sorted, and unlike the mean, it isn't pulled around by extreme values. If a dataset's mean and median are close together, the data is roughly symmetric. If they diverge noticeably, it usually signals skew \u2014 a few unusually high or low values dragging the mean away from where most of the data actually sits. Household income is a classic real-world example: the mean is pulled upward by a small number of very high earners, while the median better reflects a \"typical\" income.<\/p>\n\n<h2>Weighted average<\/h2>\n<p>A weighted average gives some values more influence than others, based on an assigned weight \u2014 the same math that powers GPA calculations, where a 4-credit course counts more than a 1-credit course. The formula sums each value multiplied by its weight, then divides by the total weight, rather than simply dividing by the count.<\/p>\n\n<h2>Geometric mean \u2014 for growth rates and ratios<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Formula<\/div>\n  <div class=\"f-eq\">GM = \u207f\u221a(x\u2081 \u00d7 x\u2082 \u00d7 ... \u00d7 x\u2099)<\/div>\n<\/div>\n<p>The geometric mean multiplies values together and takes the nth root, rather than adding and dividing. It's the mathematically correct way to average rates of change, growth rates, or investment returns over multiple periods \u2014 averaging percentage returns with a simple arithmetic mean systematically overstates actual compound growth.<\/p>\n\n<h2>Harmonic mean \u2014 for rates like speed<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Formula<\/div>\n  <div class=\"f-eq\">HM = n \u00f7 (1\/x\u2081 + 1\/x\u2082 + ... + 1\/x\u2099)<\/div>\n<\/div>\n<p>The harmonic mean is the correct average when combining rates measured over a constant unit of something else \u2014 the classic example is averaging speeds over equal distances (not equal times): if you drive 60 mph for one hour and 40 mph for the next hour, your average speed is the simple mean (50 mph); but if you drive the same distance at 60 mph and then at 40 mph, your true average speed is the harmonic mean (48 mph), since you spent more time at the slower speed.<\/p>\n\n<h2>Trimmed mean \u2014 reducing outlier influence<\/h2>\n<p>A trimmed mean removes a set number of the highest and lowest values before averaging the rest \u2014 commonly used in judged competitions (like discarding the highest and lowest scores) and in statistics when a dataset is known to contain occasional extreme errors that shouldn't be allowed to dominate the result.<\/p>\n\n<h2>Understanding standard deviation and outliers<\/h2>\n<p>This calculator's dashboard also reports standard deviation, variance, quartiles and IQR (interquartile range) alongside the mean \u2014 these describe how spread out your data is, not just its center. Values falling more than 1.5\u00d7IQR beyond the first or third quartile are flagged as potential outliers using the standard IQR rule, a widely used (though not the only) method for outlier detection.<\/p>\n\n<h2>When not to use the mean<\/h2>\n<p>Avoid relying on the arithmetic mean alone when: your data has significant outliers (use median or trimmed mean instead), you're averaging rates or ratios (use geometric or harmonic mean), values should count unequally (use weighted average), or your data is heavily skewed (report median alongside mean for a fuller picture).<\/p>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/standard-deviation\">Standard Deviation Calculator<\/a> for a deeper spread analysis, the <a href=\"#\/calculator\/gpa\">GPA Calculator<\/a> for weighted academic averages, and the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for related calculations.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do you calculate an average?<\/summary><p>Add all the values together and divide by how many values there are \u2014 this calculator does this instantly plus a full set of related statistics.<\/p><\/details>\n<details><summary>What is the difference between mean and median?<\/summary><p>The mean is the sum divided by count; the median is the middle value when sorted. The median is more resistant to outliers, while the mean uses every value directly.<\/p><\/details>\n<details><summary>When should I use a weighted average?<\/summary><p>Whenever values should count unequally \u2014 like course grades weighted by credit hours, or portfolio returns weighted by investment size.<\/p><\/details>\n<details><summary>What is a geometric mean?<\/summary><p>An average calculated by multiplying values and taking the nth root, correct for averaging growth rates, returns, and other multiplicative quantities.<\/p><\/details>\n<details><summary>What is a harmonic mean?<\/summary><p>An average calculated as the count divided by the sum of reciprocals, correct for averaging rates like speed over equal distances.<\/p><\/details>\n<details><summary>Why is the average affected by outliers?<\/summary><p>Because every value contributes directly to the sum \u2014 one very large or small value shifts the sum, and therefore the mean, even if it's the only value that far from the rest of the data.<\/p><\/details>`,\n\n'standard-deviation':`\n<h2>Standard deviation calculator \u2014 a complete statistics analyzer<\/h2>\n<p>This calculator goes beyond a single standard deviation value. It computes population and sample standard deviation side by side, full descriptive statistics (mean, median, mode, quartiles, IQR, coefficient of variation), a step-by-step calculation table, outlier detection, and both a bar chart and box plot of your data.<\/p>\n\n<h2>What is standard deviation?<\/h2>\n<p>Standard deviation measures how spread out a set of numbers is around its mean. A low standard deviation means values cluster tightly near the average; a high standard deviation means they're widely scattered. It's expressed in the same units as your original data, which makes it more directly interpretable than variance (its squared counterpart).<\/p>\n\n<h2>Population vs. sample standard deviation<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Population standard deviation (\u03c3)<\/div>\n  <div class=\"f-eq\">\u03c3 = \u221a( \u03a3(x \u2212 \u03bc)\u00b2 \u00f7 N )<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Sample standard deviation (s)<\/div>\n  <div class=\"f-eq\">s = \u221a( \u03a3(x \u2212 x\u0304)\u00b2 \u00f7 (n \u2212 1) )<\/div>\n<\/div>\n<p>Use the <b>population<\/b> formula when your data represents an entire group of interest (every student in a class, every product made this week). Use the <b>sample<\/b> formula when your data is a subset drawn from a larger population you're trying to draw conclusions about (a survey of 500 people representing a country). The sample formula divides by n\u22121 instead of n \u2014 a correction called Bessel's correction \u2014 which compensates for the fact that a sample tends to slightly underestimate the true population variance.<\/p>\n\n<h2>Step-by-step: how standard deviation is calculated<\/h2>\n<ol style=\"color:var(--text-soft);\">\n<li>Calculate the mean of all values.<\/li>\n<li>Find each value's deviation from the mean (value \u2212 mean).<\/li>\n<li>Square each deviation (this removes negative signs and weights larger deviations more heavily).<\/li>\n<li>Average the squared deviations \u2014 this is the <b>variance<\/b>. Divide by N for population, or n\u22121 for sample.<\/li>\n<li>Take the square root of the variance to return to the original units \u2014 this is the standard deviation.<\/li>\n<\/ol>\n<p>The calculation table above shows this process explicitly for every value in your dataset.<\/p>\n\n<h2>Variance vs. standard deviation<\/h2>\n<p>Variance is the average of the squared deviations \u2014 mathematically convenient for further statistical work, but hard to interpret directly since it's in squared units (e.g., squared dollars, squared kg). Standard deviation is simply the square root of variance, bringing the measure back into the original, interpretable units.<\/p>\n\n<h2>Coefficient of variation<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Formula<\/div>\n  <div class=\"f-eq\">CV = (Standard Deviation \u00f7 Mean) \u00d7 100%<\/div>\n<\/div>\n<p>Coefficient of variation expresses spread relative to the mean, making it useful for comparing variability between datasets with different units or very different averages \u2014 a standard deviation of 5 means something very different for a dataset averaging 10 versus one averaging 10,000.<\/p>\n\n<h2>Outlier detection<\/h2>\n<p>This calculator flags potential outliers using the IQR (interquartile range) rule: any value more than 1.5\u00d7IQR below Q1 or above Q3 is flagged. This is a standard, widely used heuristic \u2014 not a definitive judgment that a value is erroneous. A flagged value might be a legitimate extreme observation, a measurement error, or genuinely unusual data worth investigating further.<\/p>\n\n<h2>Real-world applications<\/h2>\n<p>Standard deviation appears throughout finance (measuring investment volatility\/risk), manufacturing quality control (process consistency), education (grade distribution analysis), and scientific research (measurement precision and reporting). A larger standard deviation in investment returns, for instance, is commonly used as a proxy for risk \u2014 though it's one measure among several, not a complete risk assessment on its own.<\/p>\n\n<h2>Common mistakes<\/h2>\n<ul>\n<li><b>Using the wrong formula<\/b> \u2014 population vs. sample confusion is the most common error; check which one your context calls for.<\/li>\n<li><b>Forgetting to square deviations<\/b> \u2014 simply averaging the raw deviations always equals zero by definition, which is why squaring is essential.<\/li>\n<li><b>Comparing standard deviations across very different means<\/b> \u2014 use the coefficient of variation instead for a fair relative comparison.<\/li>\n<li><b>Treating every flagged outlier as an error<\/b> \u2014 the IQR rule is a heuristic flag, not proof that a data point is invalid.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/average\">Average Calculator<\/a> for mean, median and additional average types, and the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for related calculations.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is standard deviation?<\/summary><p>A measure of how spread out a dataset is around its mean, expressed in the same units as the original data.<\/p><\/details>\n<details><summary>Why is sample standard deviation different from population?<\/summary><p>The sample formula divides by n\u22121 instead of n, correcting for the tendency of a sample to slightly underestimate the true population variance.<\/p><\/details>\n<details><summary>What is variance?<\/summary><p>The average of squared deviations from the mean \u2014 standard deviation is simply its square root, bringing the value back into original units.<\/p><\/details>\n<details><summary>When should I use population standard deviation?<\/summary><p>When your data represents the entire group you care about, not a sample drawn from a larger population.<\/p><\/details>\n<details><summary>What is a good standard deviation?<\/summary><p>There's no universal \"good\" value \u2014 it depends entirely on context and what's being measured; the coefficient of variation helps compare spread relative to the mean across different datasets.<\/p><\/details>\n<details><summary>Can standard deviation be zero?<\/summary><p>Yes \u2014 if and only if every value in the dataset is identical, meaning there's no spread at all.<\/p><\/details>\n<details><summary>Does a larger standard deviation always mean more risk?<\/summary><p>In finance it's a commonly used proxy for volatility, but it's one measure among several \u2014 it doesn't distinguish upside from downside variation and shouldn't be the only factor in a risk assessment.<\/p><\/details>`,\n\n'random-number':`\n<h2>Random number generator \u2014 integers, decimals, dice, names and more<\/h2>\n<p>This is a complete random generator hub, not just a min\/max\/generate box. It covers whole numbers with filters (unique, even\/odd, prime, multiples), decimal numbers, quick presets (dice, coin flips, lottery, bingo), a name picker and team splitter for classrooms and giveaways, and a random color generator \u2014 plus a statistical summary of whatever you generate.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Core method<\/div>\n  <div class=\"f-eq\">n = floor( random() \u00d7 (max \u2212 min + 1) ) + min<\/div>\n<\/div>\n<p>Every integer in your chosen range has an equal chance of appearing on each generation, drawn from a uniform distribution.<\/p>\n\n<h2>What is a random number generator?<\/h2>\n<p>A random number generator (RNG) produces values with no predictable pattern, used for anything from picking a raffle winner to shuffling a deck of cards to statistical sampling. This tool runs entirely in your browser using JavaScript's built-in <code>Math.random()<\/code> function.<\/p>\n\n<h2>Pseudo-random vs. true random numbers<\/h2>\n<p>Numbers generated here are <b>pseudo-random<\/b> \u2014 produced by a deterministic algorithm that behaves unpredictably enough for games, sampling, and everyday randomization, but is technically not \"truly\" random the way physical processes (like radioactive decay or atmospheric noise) are. True random number generators (TRNGs) derive randomness from physical phenomena and are used when genuine unpredictability is essential.<\/p>\n\n<h2>Is this suitable for cryptographic purposes?<\/h2>\n<p>No. <code>Math.random()<\/code>, the source used here, is explicitly not designed for security-sensitive applications like generating encryption keys, security tokens, or passwords resistant to prediction attacks \u2014 its internal algorithm can, in principle, be reverse-engineered from enough output. For anything cryptographic, use a source specifically designed for it (like the browser's <code>crypto.getRandomValues()<\/code> API) \u2014 our own <a href=\"#\/calculator\/password\">Password Generator<\/a> is built for that purpose specifically.<\/p>\n\n<h2>Applications of random number generation<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Field<\/th><th>Common use<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Gaming<\/td><td>Dice rolls, card shuffling, loot drops, procedural content<\/td><\/tr>\n    <tr><td>Education<\/td><td>Fair student selection, randomized quiz questions, classroom groupings<\/td><\/tr>\n    <tr><td>Statistics<\/td><td>Random sampling, simulations, Monte Carlo methods<\/td><\/tr>\n    <tr><td>Business<\/td><td>Raffle and giveaway winner selection, A\/B test assignment<\/td><\/tr>\n    <tr><td>Testing\/development<\/td><td>Generating test data, fuzzing inputs<\/td><\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>Common myths about randomness<\/h2>\n<p>A common misconception is that random results should \"even out\" quickly or avoid repeats \u2014 in reality, true randomness often produces streaks and clusters that look non-random (the \"gambler's fallacy\"). Seeing the same number twice in a row, or several odd numbers in sequence, is entirely expected in genuinely random output and doesn't indicate a flaw.<\/p>\n\n<h2>Related tools<\/h2>\n<p>Need random characters instead of numbers? Try the <a href=\"#\/calculator\/password\">Password Generator<\/a>. For deeper analysis of generated numbers, see the <a href=\"#\/calculator\/average\">Average Calculator<\/a> and <a href=\"#\/calculator\/standard-deviation\">Standard Deviation Calculator<\/a>.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>Is this random number generator truly random?<\/summary><p>It's pseudo-random, generated by your browser's built-in algorithm \u2014 unpredictable enough for games, sampling and everyday use, but not a physical \"true\" random source.<\/p><\/details>\n<details><summary>Can I generate unique numbers?<\/summary><p>Yes \u2014 enable \"Unique only\" in Integer mode to guarantee no repeats within a single generation.<\/p><\/details>\n<details><summary>Can I exclude duplicate values?<\/summary><p>Yes, the unique-only option does exactly this; without it, duplicates are possible and are reported in the statistical summary.<\/p><\/details>\n<details><summary>Can I generate negative numbers?<\/summary><p>Yes \u2014 set a negative minimum value (e.g., -100 to 100) in either Integer or Decimal mode.<\/p><\/details>\n<details><summary>Can I generate decimal numbers?<\/summary><p>Yes \u2014 switch to Decimal mode and choose how many decimal places you want, from 0 to 10.<\/p><\/details>\n<details><summary>Is the generator suitable for cryptographic purposes?<\/summary><p>No \u2014 it uses standard pseudo-random generation, not a cryptographically secure source. Use a purpose-built tool like our Password Generator for security-sensitive needs.<\/p><\/details>\n<details><summary>Can I save or export my results?<\/summary><p>Yes \u2014 copy results to your clipboard or download them as a CSV file; recent generations are also listed in the session history.<\/p><\/details>`,\n\ntriangle:`\n<h2>Triangle calculator \u2014 solve by SSS, SAS, ASA, right-triangle or coordinates<\/h2>\n<p>This calculator solves a triangle from whichever information you have \u2014 three sides, two sides and an included angle, two angles and an included side, right-triangle legs or hypotenuse, or three coordinate points \u2014 and returns a complete set of properties: all sides and angles, area, perimeter, altitudes, medians, inradius, circumradius, and a full classification, with step-by-step working and a live diagram.<\/p>\n\n<h2>What is a triangle?<\/h2>\n<p>A triangle is the simplest possible polygon \u2014 three straight sides connecting three vertices, with interior angles that always sum to exactly 180\u00b0. Every triangle, regardless of shape, can be fully determined (\"solved\") from just three independent pieces of information, provided at least one is a side length \u2014 which is why SSS, SAS, and ASA are each sufficient starting points.<\/p>\n\n<h2>Triangle classification<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>By sides<\/th><th>By angles<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td><b>Equilateral<\/b> \u2014 all three sides equal<\/td><td><b>Acute<\/b> \u2014 all angles under 90\u00b0<\/td><\/tr>\n    <tr><td><b>Isosceles<\/b> \u2014 exactly two sides equal<\/td><td><b>Right<\/b> \u2014 one angle exactly 90\u00b0<\/td><\/tr>\n    <tr><td><b>Scalene<\/b> \u2014 all sides different<\/td><td><b>Obtuse<\/b> \u2014 one angle over 90\u00b0<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>Every triangle you solve here is automatically classified both ways \u2014 for example, a 3-4-5 triangle is scalene (all different sides) and right (one 90\u00b0 angle).<\/p>\n\n<h2>The Triangle Inequality Theorem<\/h2>\n<p>Three lengths only form a valid triangle if each side is shorter than the sum of the other two \u2014 otherwise the \"triangle\" can't physically close. This calculator checks this automatically for SSS and coordinate inputs and explains why an invalid combination fails.<\/p>\n\n<h2>Heron's formula (SSS)<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Area from three sides<\/div>\n  <div class=\"f-eq\">s = (a+b+c) \u00f7 2 &nbsp;|&nbsp; Area = \u221a( s(s\u2212a)(s\u2212b)(s\u2212c) )<\/div>\n<\/div>\n<p>Heron's formula finds a triangle's area directly from its three side lengths, without needing to know any angle first \u2014 \"s\" here is the semi-perimeter (half the perimeter).<\/p>\n\n<h2>Law of Cosines and Law of Sines<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Law of Cosines (used for SSS and SAS)<\/div>\n  <div class=\"f-eq\">c\u00b2 = a\u00b2 + b\u00b2 \u2212 2ab\u00b7cos(C)<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Law of Sines (used for ASA and AAS)<\/div>\n  <div class=\"f-eq\">a \u00f7 sin(A) = b \u00f7 sin(B) = c \u00f7 sin(C)<\/div>\n<\/div>\n<p>The Law of Cosines generalizes the Pythagorean theorem to any triangle, useful whenever you know two sides and the angle between them (SAS), or all three sides (SSS). The Law of Sines relates each side to the sine of its opposite angle, useful whenever you know two angles and a side (ASA\/AAS) \u2014 since knowing two angles immediately gives you the third (they must sum to 180\u00b0).<\/p>\n\n<h2>The Pythagorean theorem (right triangles)<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Right triangles only<\/div>\n  <div class=\"f-eq\">a\u00b2 + b\u00b2 = c\u00b2 (c = hypotenuse)<\/div>\n<\/div>\n<p>The Pythagorean theorem is a special case of the Law of Cosines that applies only when one angle is exactly 90\u00b0 (making cos(90\u00b0) = 0, which eliminates the last term). It's the fastest way to solve a right triangle from two known sides.<\/p>\n\n<h2>Triangle centers<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Center<\/th><th>Definition<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Centroid<\/td><td>Intersection of the three medians \u2014 the triangle's \"center of mass\"<\/td><\/tr>\n    <tr><td>Incenter<\/td><td>Intersection of angle bisectors \u2014 center of the inscribed circle (inradius)<\/td><\/tr>\n    <tr><td>Circumcenter<\/td><td>Intersection of perpendicular bisectors \u2014 center of the circumscribed circle (circumradius)<\/td><\/tr>\n    <tr><td>Orthocenter<\/td><td>Intersection of the three altitudes<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>This calculator reports the inradius and circumradius (and centroid for coordinate input) directly \u2014 the incenter, circumcenter and orthocenter coincide at the same point only in an equilateral triangle; in any other triangle they're three distinct points.<\/p>\n\n<h2>When to use the Law of Sines vs. Law of Cosines<\/h2>\n<p>Use the <b>Law of Cosines<\/b> when you know three sides (SSS) or two sides and the included angle (SAS) \u2014 situations where the Law of Sines alone can't get started. Use the <b>Law of Sines<\/b> when you know two angles and a side (ASA\/AAS) \u2014 it's simpler algebra once you have an angle-side pair to work from.<\/p>\n\n<h2>Real-world applications<\/h2>\n<p>Triangle geometry underlies land surveying (triangulation for mapping and GPS), architecture and construction (roof pitches, structural triangulation for stability), navigation (bearing and distance calculations), and engineering (force analysis, truss design) \u2014 the same core formulas covered here.<\/p>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/scientific\">Scientific Calculator<\/a> for standalone trigonometric functions, and the <a href=\"#\/calculator\/unit-conversion\">Unit Conversion Calculator<\/a> for converting between measurement units.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I solve a triangle?<\/summary><p>Provide any valid combination of three measurements (three sides, two sides and an angle, or two angles and a side) and this calculator solves for everything else automatically.<\/p><\/details>\n<details><summary>What is Heron's formula?<\/summary><p>A formula that calculates a triangle's area directly from its three side lengths, without needing to know any angle first.<\/p><\/details>\n<details><summary>What is the Triangle Inequality Theorem?<\/summary><p>The rule that each side of a triangle must be shorter than the sum of the other two sides \u2014 otherwise the shape can't close into a valid triangle.<\/p><\/details>\n<details><summary>How do I calculate triangle area?<\/summary><p>Several ways: Heron's formula from three sides, \u00bd \u00d7 base \u00d7 height, or \u00bdab\u00b7sin(C) from two sides and the included angle \u2014 this calculator uses whichever fits your inputs.<\/p><\/details>\n<details><summary>Can a triangle have two right angles?<\/summary><p>No \u2014 since all three angles must sum to exactly 180\u00b0, two 90\u00b0 angles would leave nothing for the third angle, which is geometrically impossible.<\/p><\/details>\n<details><summary>What is the difference between the centroid and circumcenter?<\/summary><p>The centroid is the intersection of the medians (the triangle's balance point); the circumcenter is the intersection of the perpendicular bisectors (equidistant from all three vertices) \u2014 they coincide only in an equilateral triangle.<\/p><\/details>\n<details><summary>When should I use the Law of Sines instead of the Law of Cosines?<\/summary><p>Use the Law of Sines when you have an angle-side pair from ASA or AAS information; use the Law of Cosines when you have SSS or SAS information instead.<\/p><\/details>`,\n\nquadratic:`\n<h2>Quadratic equation calculator \u2014 solve, graph, and understand<\/h2>\n<p>This calculator does more than return two roots. It shows the discriminant analysis, exact and decimal root forms, factored and vertex forms, a full parabola analysis (vertex, axis of symmetry, domain, range), step-by-step working, root verification, and an interactive graph \u2014 plus optional sliders to explore how changing a, b, or c reshapes the parabola in real time.<\/p>\n\n<h2>What is a quadratic equation?<\/h2>\n<p>A quadratic equation is any equation that can be written in the standard form <b>ax\u00b2 + bx + c = 0<\/b>, where a, b and c are constants and a \u2260 0 (if a were 0, the x\u00b2 term would vanish and it would just be a linear equation). Its graph is always a parabola \u2014 a symmetric U-shaped curve that opens upward when a &gt; 0 and downward when a &lt; 0.<\/p>\n\n<h2>The quadratic formula<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Formula<\/div>\n  <div class=\"f-eq\">x = ( \u2212b \u00b1 \u221a(b\u00b2 \u2212 4ac) ) \u00f7 2a<\/div>\n<\/div>\n<p>This formula solves any quadratic equation, regardless of whether it factors nicely \u2014 it's derived by completing the square on the general standard form, and works identically whether the roots turn out to be real or complex.<\/p>\n\n<h2>The discriminant<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Discriminant<\/div>\n  <div class=\"f-eq\">D = b\u00b2 \u2212 4ac<\/div>\n<\/div>\n<table class=\"ref-table\">\n  <thead><tr><th>Discriminant<\/th><th>Root type<\/th><th>Graph behavior<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>D &gt; 0<\/td><td>Two distinct real roots<\/td><td>Parabola crosses the x-axis at two points<\/td><\/tr>\n    <tr><td>D = 0<\/td><td>One repeated real root<\/td><td>Parabola touches the x-axis at exactly one point (the vertex)<\/td><\/tr>\n    <tr><td>D &lt; 0<\/td><td>Two complex conjugate roots<\/td><td>Parabola never crosses the x-axis<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>The discriminant alone tells you the nature of the solutions before you even finish solving \u2014 this calculator computes and explains it automatically for every equation.<\/p>\n\n<h2>Why are there two solutions?<\/h2>\n<p>The \u00b1 in the quadratic formula reflects that squaring a positive or negative number gives the same result \u2014 so there are generally two x-values that satisfy the equation, symmetric around the vertex. Geometrically, both roots are where the parabola crosses the x-axis (when real), which is why a U-shaped curve typically intersects a horizontal line at two points.<\/p>\n\n<h2>Completing the square and the vertex<\/h2>\n<p>\"Completing the square\" rewrites ax\u00b2 + bx + c into vertex form, a(x \u2212 h)\u00b2 + k, where (h, k) is the parabola's vertex \u2014 its minimum point (if a &gt; 0) or maximum point (if a &lt; 0). The vertex lies on the parabola's axis of symmetry, the vertical line x = h that divides the curve into two mirror-image halves.<\/p>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Vertex coordinates<\/div>\n  <div class=\"f-eq\">h = \u2212b \u00f7 2a &nbsp;|&nbsp; k = f(h) = a\u00b7h\u00b2 + b\u00b7h + c<\/div>\n<\/div>\n\n<h2>Factoring vs. the quadratic formula<\/h2>\n<p>Factoring is faster when an equation has \"nice\" rational roots (like x\u00b2 \u2212 5x + 6 = (x\u22122)(x\u22123)), but not every quadratic factors cleanly with integers \u2014 irrational or complex roots simply can't be found by guess-and-check factoring. The quadratic formula always works, which is why it's the reliable fallback method regardless of whether factoring is feasible.<\/p>\n\n<h2>Domain, range, and concavity<\/h2>\n<p>Every quadratic function has a domain of all real numbers (you can plug in any x). Its range depends on the vertex and direction of opening: if a &gt; 0 (opens upward), the range is y \u2265 k; if a &lt; 0 (opens downward), the range is y \u2264 k, where k is the vertex's y-coordinate.<\/p>\n\n<h2>Real-world applications<\/h2>\n<p>Quadratic equations model projectile motion (the parabolic path of a thrown object under gravity), business profit optimization (finding the price point that maximizes revenue), area optimization problems (maximizing enclosed area for a fixed perimeter), and structural engineering (parabolic arches and cable curves).<\/p>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/scientific\">Scientific Calculator<\/a> for standalone algebraic evaluation, and the <a href=\"#\/calculator\/triangle\">Triangle Calculator<\/a> for related geometry.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is a quadratic equation?<\/summary><p>Any equation of the form ax\u00b2 + bx + c = 0 where a \u2260 0 \u2014 its graph is always a parabola.<\/p><\/details>\n<details><summary>How do I solve a quadratic equation?<\/summary><p>The quadratic formula always works: x = (\u2212b \u00b1 \u221a(b\u00b2\u22124ac)) \u00f7 2a \u2014 this calculator applies it automatically along with factoring and vertex-form analysis.<\/p><\/details>\n<details><summary>What is the discriminant?<\/summary><p>b\u00b2 \u2212 4ac \u2014 its sign tells you whether the roots are two distinct reals, one repeated real, or a complex conjugate pair, before you finish solving.<\/p><\/details>\n<details><summary>What if the discriminant is negative?<\/summary><p>The equation has no real solutions \u2014 the two roots are complex conjugates, and the parabola never crosses the x-axis.<\/p><\/details>\n<details><summary>What is the vertex?<\/summary><p>The turning point of the parabola \u2014 its minimum (if it opens upward) or maximum (if it opens downward), lying on the axis of symmetry.<\/p><\/details>\n<details><summary>Why are there two solutions?<\/summary><p>Because the quadratic formula's \u00b1 reflects that a parabola generally crosses a horizontal line (like the x-axis) at two symmetric points around its vertex.<\/p><\/details>\n<details><summary>What is the difference between factoring and the quadratic formula?<\/summary><p>Factoring is quicker for equations with clean rational roots but doesn't work for every equation; the quadratic formula solves any quadratic equation regardless of whether it factors nicely.<\/p><\/details>`,\n\nscientific:`\n<h2>Scientific calculator \u2014 full function support with history and memory<\/h2>\n<p>Type a full expression and this calculator evaluates it instantly, supporting standard arithmetic, trigonometry (direct and inverse), logarithms, roots, factorials, combinatorics, and more \u2014 with a switchable angle mode, memory functions (MC\/MR\/MS\/M+\/M\u2212), a calculation history you can click to reuse, and typo suggestions when a function name doesn't match.<\/p>\n\n<h2>Order of operations<\/h2>\n<p>Expressions are evaluated using the standard PEMDAS\/BODMAS order: <b>P<\/b>arentheses first, then <b>E<\/b>xponents, then <b>M<\/b>ultiplication and <b>D<\/b>ivision (left to right), then <b>A<\/b>ddition and <b>S<\/b>ubtraction (left to right). Parentheses always take priority, which is why they're essential for controlling exactly how a complex expression is evaluated.<\/p>\n\n<h2>Supported functions<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Function<\/th><th>Meaning<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>sqrt(x), cbrt(x)<\/td><td>Square root and cube root<\/td><\/tr>\n    <tr><td>sin(x), cos(x), tan(x)<\/td><td>Trig functions \u2014 input interpreted per current angle mode<\/td><\/tr>\n    <tr><td>asin(x), acos(x), atan(x)<\/td><td>Inverse trig \u2014 output returned per current angle mode<\/td><\/tr>\n    <tr><td>sinh(x), cosh(x), tanh(x)<\/td><td>Hyperbolic functions (radians)<\/td><\/tr>\n    <tr><td>log(x), ln(x), log2(x)<\/td><td>Base-10, natural (base e), and base-2 logarithms<\/td><\/tr>\n    <tr><td>n!<\/td><td>Factorial \u2014 the product of all positive integers up to n<\/td><\/tr>\n    <tr><td>nPr(n,r), nCr(n,r)<\/td><td>Permutations and combinations<\/td><\/tr>\n    <tr><td>abs(x), mod(a,b)<\/td><td>Absolute value, and remainder of a\u00f7b<\/td><\/tr>\n    <tr><td>^<\/td><td>Exponent (power)<\/td><\/tr>\n    <tr><td>pi, e, phi<\/td><td>Mathematical constants \u03c0, e, and the golden ratio \u03c6<\/td><\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>Degree vs. radian (vs. gradian) mode<\/h2>\n<p>Trigonometric functions need an angle unit to interpret their input correctly. <b>Degrees<\/b> (0\u2013360\u00b0 per full circle) is the everyday standard. <b>Radians<\/b> (0\u20132\u03c0 per full circle) is the standard unit in higher mathematics and calculus, since it makes many formulas simpler. <b>Gradians<\/b> (0\u2013400 per full circle) are less common but still used in some surveying and engineering contexts. This calculator's angle mode selector applies consistently to both direct trig functions (sin, cos, tan) and their inverses (asin, acos, atan) \u2014 switching modes changes both how input is interpreted and how inverse-function output is displayed.<\/p>\n\n<h2>Logarithms explained<\/h2>\n<p><b>log(x)<\/b> here means base-10 logarithm (how many times you multiply 10 by itself to reach x). <b>ln(x)<\/b> is the natural logarithm, base e (Euler's number, \u22482.71828), fundamental in calculus and growth\/decay modeling. <b>log2(x)<\/b> is base-2, common in computer science. All three answer the same underlying question \u2014 \"what power do I raise this base to?\" \u2014 just with different bases.<\/p>\n\n<h2>Factorials and combinatorics<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Factorial<\/div>\n  <div class=\"f-eq\">n! = n \u00d7 (n\u22121) \u00d7 (n\u22122) \u00d7 ... \u00d7 2 \u00d7 1<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Permutations and combinations<\/div>\n  <div class=\"f-eq\">nPr = n! \u00f7 (n\u2212r)! &nbsp;|&nbsp; nCr = n! \u00f7 (r!(n\u2212r)!)<\/div>\n<\/div>\n<p>Factorial counts the number of ways to arrange n distinct items in order. <b>nPr<\/b> counts ordered selections of r items from n (permutations); <b>nCr<\/b> counts unordered selections (combinations) \u2014 nCr is always smaller than nPr for the same n and r, since it doesn't count different orderings of the same selected items separately.<\/p>\n\n<h2>Scientific notation<\/h2>\n<p>Very large or very small results are often expressed in scientific notation (like 6.022 \u00d7 10\u00b2\u00b3) to stay readable. While this calculator displays results as standard decimals, you can enter powers directly using the ^ operator \u2014 for example, <code>6.022*10^23<\/code> \u2014 to work with scientific-notation-scale values.<\/p>\n\n<h2>Memory functions<\/h2>\n<p>Just like a physical calculator: <b>MS<\/b> stores the current result in memory, <b>MR<\/b> recalls it into your expression, <b>M+<\/b> and <b>M\u2212<\/b> add or subtract the current result from memory, and <b>MC<\/b> clears memory. A small \"M\" badge appears next to the input whenever memory holds a non-zero value.<\/p>\n\n<h2>Common mistakes<\/h2>\n<ul>\n<li><b>Forgetting the angle mode<\/b> \u2014 sin(30) means very different things in degree mode (0.5) versus radian mode (\u2248 \u22120.988); always check which mode you're in.<\/li>\n<li><b>Mismatched parentheses<\/b> \u2014 every opening parenthesis needs a matching close; unbalanced parentheses are the most common syntax error.<\/li>\n<li><b>Confusing log and ln<\/b> \u2014 log(x) here is base-10, not natural log; use ln(x) specifically for base-e.<\/li>\n<\/ul>\n\n<h2>Related calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/quadratic\">Quadratic Equation Calculator<\/a> for polynomial solving, the <a href=\"#\/calculator\/triangle\">Triangle Calculator<\/a> for geometry, the <a href=\"#\/calculator\/fraction\">Fraction Calculator<\/a> for exact fraction arithmetic, and the <a href=\"#\/calculator\/unit-conversion\">Unit Conversion Calculator<\/a> for measurement conversions.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I use a scientific calculator?<\/summary><p>Type a full expression (like <code>sin(45) + sqrt(16)<\/code>) and press Evaluate, or build it using the button pad \u2014 results, history, and memory work just like a physical scientific calculator.<\/p><\/details>\n<details><summary>What is DEG mode?<\/summary><p>Degree mode, where a full circle is 360\u00b0 \u2014 the everyday standard for angles, selected by default here.<\/p><\/details>\n<details><summary>What is RAD mode?<\/summary><p>Radian mode, where a full circle is 2\u03c0 radians \u2014 the standard in calculus and advanced mathematics.<\/p><\/details>\n<details><summary>How do I calculate logarithms?<\/summary><p>Use log(x) for base-10, ln(x) for natural log (base e), or log2(x) for base-2 \u2014 all three are supported directly in expressions.<\/p><\/details>\n<details><summary>How do I enter powers?<\/summary><p>Use the ^ symbol \u2014 for example, <code>2^10<\/code> evaluates to 1024.<\/p><\/details>\n<details><summary>What is scientific notation?<\/summary><p>A way of writing very large or small numbers as a value between 1 and 10 times a power of 10 \u2014 enter it directly using ^, like <code>3.5*10^8<\/code>.<\/p><\/details>\n<details><summary>How do I calculate trigonometric functions?<\/summary><p>Use sin(), cos(), or tan() with your angle mode set correctly first \u2014 switch between Degrees, Radians, and Gradians using the selector above the expression field.<\/p><\/details>`,\n\nbmi:`\n<h2>BMI calculator \u2014 Body Mass Index with healthy weight range and target planning<\/h2>\n<p>This BMI calculator does more than return a single number. It shows where you fall on a visual scale, your healthy weight range, a target weight planner, an optional waist-to-height ratio, and \u2014 if you share your age, sex and activity level \u2014 an estimate of your daily energy needs. Every part of this tool is a general screening estimate, not a medical diagnosis.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Formula<\/div>\n  <div class=\"f-eq\">BMI = weight (kg) \u00f7 height (m)\u00b2<\/div>\n<\/div>\n<p>In imperial units, the equivalent formula is BMI = 703 \u00d7 weight (lb) \u00f7 height (in)\u00b2. Both forms produce the same result \u2014 this calculator handles the conversion automatically based on the unit toggle.<\/p>\n\n<h2>What is BMI?<\/h2>\n<p>Body Mass Index is a simple ratio of weight to height, developed in the 19th century and later adopted by health organizations worldwide as a quick, low-cost population screening tool. It doesn't measure body fat directly \u2014 it's a proxy that correlates reasonably well with body fat percentage across large populations, which is exactly why it's useful for public health statistics but limited for any one individual.<\/p>\n\n<h2>BMI categories<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>BMI range<\/th><th>Category<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Below 18.5<\/td><td>Underweight<\/td><\/tr>\n    <tr><td>18.5 \u2013 24.9<\/td><td>Healthy weight<\/td><\/tr>\n    <tr><td>25.0 \u2013 29.9<\/td><td>Overweight<\/td><\/tr>\n    <tr><td>30.0 and above<\/td><td>Obesity<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>These bands are the standard adult classification used by major health organizations, and are what this calculator uses to determine your healthy weight range and category.<\/p>\n\n<h2>BMI's limitations \u2014 why it isn't the full picture<\/h2>\n<p>BMI cannot distinguish muscle from fat. A muscular athlete and a sedentary person of the same height and weight will have identical BMI scores despite very different body compositions \u2014 this is the single most common criticism of BMI, and a fair one. It also doesn't account for where fat is distributed on the body (which matters for health risk), bone density, age-related muscle loss, pregnancy, or ethnic variation in body composition norms. This is exactly why this calculator also offers a waist-to-height ratio and links to a dedicated Body Fat Calculator \u2014 using more than one indicator gives a fuller picture than BMI alone.<\/p>\n\n<h2>Can athletes have a high BMI?<\/h2>\n<p>Yes, routinely. Muscle is denser than fat, so athletes and heavily muscled individuals often score in the \"overweight\" or even \"obesity\" BMI range despite having low body fat and excellent health markers. If this describes your situation, BMI alone is a poor indicator for you specifically \u2014 body fat percentage and waist-to-height ratio are more informative.<\/p>\n\n<h2>Does BMI differ by age or sex?<\/h2>\n<p>The standard adult BMI categories used here apply the same thresholds to adult men and women, though body fat percentage at a given BMI does tend to differ somewhat by sex and increases with age even at a stable BMI, due to natural changes in muscle mass. Children and teenagers should not use adult BMI categories at all \u2014 pediatric BMI is assessed using age- and sex-specific percentile charts rather than fixed thresholds, since kids' bodies change proportionally as they grow.<\/p>\n\n<h2>Healthy weight range and target weight planning<\/h2>\n<p>Your healthy weight range is simply the weight span corresponding to a BMI of 18.5\u201324.9 at your height. The target weight planner lets you pick any BMI within that healthy range and see the corresponding weight \u2014 useful for setting a realistic, evidence-informed goal rather than an arbitrary number.<\/p>\n\n<h2>Waist-to-height ratio<\/h2>\n<p>Dividing waist circumference by height is increasingly used alongside BMI because it captures something BMI misses: where fat is carried. A ratio below 0.5 is generally associated with lower health risk in population studies, though like BMI, it's a general indicator rather than an individual diagnosis.<\/p>\n\n<h2>Healthy lifestyle basics<\/h2>\n<ul>\n<li><b>Balanced nutrition<\/b> \u2014 a varied diet with adequate protein, fiber, fruits and vegetables supports steady energy and long-term health more reliably than restrictive short-term approaches.<\/li>\n<li><b>Regular physical activity<\/b> \u2014 most health guidelines suggest at least 150 minutes of moderate activity per week for general health, alongside some strength training.<\/li>\n<li><b>Sleep and stress<\/b> \u2014 both significantly affect weight regulation and overall health, and are often overlooked in favor of diet and exercise alone.<\/li>\n<li><b>When to see a professional<\/b> \u2014 if your BMI is outside the healthy range, if you have concerns about your weight or health, or if you're planning significant lifestyle changes, a doctor or registered dietitian can give guidance tailored to your actual health history \u2014 something no calculator can do.<\/li>\n<\/ul>\n\n<h2>Related health calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/bmr\">BMR Calculator<\/a> for resting energy needs, the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for daily intake estimates by activity level, the <a href=\"#\/calculator\/body-fat\">Body Fat Calculator<\/a> for a composition-based estimate, and the <a href=\"#\/calculator\/ideal-weight\">Ideal Weight Calculator<\/a> for alternative formula comparisons.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is BMI?<\/summary><p>Body Mass Index is a ratio of weight to height used as a general population-level screening indicator for weight categories.<\/p><\/details>\n<details><summary>Is BMI accurate?<\/summary><p>It's reasonably accurate as a population-level statistic, but limited for individuals with high muscle mass, certain body types, or during pregnancy \u2014 it's best used alongside other measures, not alone.<\/p><\/details>\n<details><summary>What is a healthy BMI?<\/summary><p>18.5 to 24.9 is the standard healthy range used by major health organizations for adults.<\/p><\/details>\n<details><summary>Can athletes have a high BMI?<\/summary><p>Yes \u2014 muscle is denser than fat, so muscular individuals often score higher on BMI despite low body fat and strong health markers.<\/p><\/details>\n<details><summary>Does age affect BMI?<\/summary><p>The category thresholds stay the same for adults, but body composition at a given BMI naturally shifts with age as muscle mass tends to decrease over time.<\/p><\/details>\n<details><summary>Is BMI different for men and women?<\/summary><p>The standard categories apply to both, though body fat percentage at the same BMI tends to differ somewhat between men and women on average.<\/p><\/details>\n<details><summary>Should children use the same BMI calculator?<\/summary><p>No \u2014 children and teens should be assessed with age- and sex-specific pediatric BMI percentile charts, not the fixed adult categories used here.<\/p><\/details>`,\n\n\nbmr:`\n<h2>BMR calculator \u2014 Basal Metabolic Rate and daily energy planning<\/h2>\n<p>This BMR calculator goes beyond a single number. It compares three established formulas side by side, converts your BMR into daily calorie needs (TDEE) across five activity levels, builds a goal-based calorie plan, estimates a weight-goal timeline, suggests macronutrient targets, and shows how common activities compare to your daily energy burn \u2014 all from one set of inputs.<\/p>\n\n<h2>What is BMR?<\/h2>\n<p>Basal Metabolic Rate is the number of calories your body burns at complete rest just to keep essential functions running \u2014 breathing, circulation, cell repair, and maintaining body temperature. It typically accounts for the majority of most people's total daily calorie burn, even before any deliberate exercise or activity is added on top.<\/p>\n\n<h2>BMR vs. TDEE \u2014 what's the difference?<\/h2>\n<p><b>BMR<\/b> is your energy burn at complete rest. <b>TDEE<\/b> (Total Daily Energy Expenditure) is your BMR multiplied by an activity factor to account for everything else you do in a day \u2014 walking, working, exercising, even digesting food. TDEE is the number that actually matters for calorie planning, which is why this calculator shows both, along with the multiplier used at each activity level.<\/p>\n\n<h2>BMR formulas compared<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Mifflin-St Jeor (default, most broadly recommended)<\/div>\n  <div class=\"f-eq\">Men: 10W + 6.25H \u2212 5A + 5 &nbsp;|&nbsp; Women: 10W + 6.25H \u2212 5A \u2212 161<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Harris-Benedict (Revised)<\/div>\n  <div class=\"f-eq\">Men: 88.362 + 13.397W + 4.799H \u2212 5.677A &nbsp;|&nbsp; Women: 447.593 + 9.247W + 3.098H \u2212 4.330A<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Katch-McArdle (requires body fat %)<\/div>\n  <div class=\"f-eq\">BMR = 370 + 21.6 \u00d7 Lean Body Mass (kg)<\/div>\n<\/div>\n<p>W is weight in kg, H is height in cm, A is age in years. Mifflin-St Jeor is generally considered the most accurate for the general population today and is used as this calculator's default. Katch-McArdle can be more precise for people who know their body fat percentage, since it's based directly on lean mass rather than total weight \u2014 useful for anyone with an athletic build, where total-weight-based formulas tend to overestimate or underestimate less accurately.<\/p>\n\n<h2>Factors that affect metabolism<\/h2>\n<ul>\n<li><b>Muscle mass<\/b> \u2014 muscle tissue burns more calories at rest than fat tissue, so two people at the same weight can have meaningfully different BMRs depending on body composition.<\/li>\n<li><b>Age<\/b> \u2014 BMR tends to decline gradually with age, largely due to natural reductions in muscle mass over time rather than age itself.<\/li>\n<li><b>Sex<\/b> \u2014 on average, men tend to have higher BMR than women at the same weight and height, primarily due to typical differences in muscle mass and body composition.<\/li>\n<li><b>Body size<\/b> \u2014 larger bodies generally burn more calories at rest simply because there's more tissue to maintain.<\/li>\n<li><b>Genetics and hormones<\/b> \u2014 thyroid function and individual genetic variation both influence metabolic rate, which is part of why formula-based estimates are approximations, not exact measurements.<\/li>\n<\/ul>\n\n<h2>Common metabolism myths<\/h2>\n<p>\"Slow metabolism\" is often blamed for weight gain, but individual metabolic rate differences are usually smaller than commonly believed \u2014 total calorie intake and activity level typically matter more. Similarly, claims of dramatically \"boosting\" metabolism through specific foods or supplements are generally overstated; the most reliable ways to meaningfully affect BMR are building muscle mass through resistance training and, to a lesser degree, maintaining adequate activity throughout the day.<\/p>\n\n<h2>Safe approaches to weight management<\/h2>\n<p>A moderate calorie deficit (this calculator defaults goal options in the 250-750 kcal\/day range) is generally easier to sustain than an aggressive one, and is less likely to trigger the muscle loss and metabolic slowdown associated with very low-calorie diets. Including resistance training helps preserve lean muscle during a deficit, which helps keep BMR from dropping as much as it otherwise might. Very low-calorie diets, rapid weight loss goals, or any significant dietary change should be discussed with a healthcare professional, especially for anyone with existing health conditions.<\/p>\n\n<h2>Related health calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/bmi\">BMI Calculator<\/a> for a healthy weight range, the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for a simpler daily-needs estimate, the <a href=\"#\/calculator\/body-fat\">Body Fat Calculator<\/a> to enable the Katch-McArdle formula, and the <a href=\"#\/calculator\/ideal-weight\">Ideal Weight Calculator<\/a> for alternative target-weight formulas.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is BMR?<\/summary><p>Basal Metabolic Rate is the number of calories your body burns at complete rest to maintain essential functions like breathing and circulation.<\/p><\/details>\n<details><summary>Is BMR the same as TDEE?<\/summary><p>No \u2014 BMR is your resting energy burn; TDEE adds activity on top of BMR and is the figure most relevant for daily calorie planning.<\/p><\/details>\n<details><summary>Which BMR formula is most accurate?<\/summary><p>Mifflin-St Jeor is generally considered the most accurate for the general population; Katch-McArdle can be more precise if you know your body fat percentage.<\/p><\/details>\n<details><summary>Does age lower BMR?<\/summary><p>Yes, generally \u2014 largely because of natural reductions in muscle mass with age rather than age itself directly.<\/p><\/details>\n<details><summary>Does muscle increase BMR?<\/summary><p>Yes \u2014 muscle tissue burns more calories at rest than fat tissue, so more lean mass generally means a higher BMR at the same body weight.<\/p><\/details>\n<details><summary>How many calories should I eat to lose weight?<\/summary><p>A common approach is a moderate deficit below your TDEE (roughly 250-500 kcal\/day for gradual loss) \u2014 see the goal-based calorie planner above for estimates at your specific TDEE.<\/p><\/details>\n<details><summary>Can I increase my metabolism?<\/summary><p>Building muscle mass through resistance training is the most reliable lever; claims about specific \"metabolism-boosting\" foods or supplements are generally overstated in effect size.<\/p><\/details>`,\n\n\ncalorie:`\n<h2>Calorie calculator \u2014 daily calorie needs and nutrition planning<\/h2>\n<p>This calculator answers more than \"how many calories do I need.\" It builds a complete nutrition plan: calorie targets across six goals at once, a weight-loss timeline comparing three paces side by side, macronutrient targets across six dietary styles, a suggested meal-by-meal calorie split, a water intake estimate, and food-energy comparisons that put calorie numbers in everyday context.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Core calculation<\/div>\n  <div class=\"f-eq\">TDEE = BMR \u00d7 Activity multiplier &nbsp;|&nbsp; Target calories = TDEE \u00b1 Goal adjustment<\/div>\n<\/div>\n\n<h2>What are calories, really?<\/h2>\n<p>A calorie is a unit of energy \u2014 specifically, the energy your body gets from food and uses for everything from breathing to walking to digesting the food itself. Your calorie needs aren't a fixed number; they scale with your body size, activity level, age, sex, and to a lesser extent genetics, which is exactly why this calculator asks for those specific inputs rather than giving one generic figure.<\/p>\n\n<h2>BMR vs. TDEE \u2014 the two numbers behind your target<\/h2>\n<p><b>BMR<\/b> (Basal Metabolic Rate) is the energy your body burns at complete rest. <b>TDEE<\/b> (Total Daily Energy Expenditure) multiplies BMR by an activity factor to estimate your true daily burn including movement and exercise. Your calorie target is then TDEE adjusted up or down depending on your goal \u2014 this calculator shows all three numbers together so you can see exactly how your target was derived, not just the final figure.<\/p>\n\n<h2>Calorie deficits and surpluses<\/h2>\n<p>Weight loss generally requires consistently eating fewer calories than you burn (a deficit); weight gain requires consistently eating more (a surplus). The commonly cited approximation is that roughly 7,700 kcal corresponds to about 1 kg of body weight change, which is what powers the weight-loss timeline comparison above \u2014 showing mild, moderate and faster paces side by side so you can weigh the trade-off between speed and sustainability yourself.<\/p>\n\n<h2>Why moderate deficits tend to work better long-term<\/h2>\n<p>A very large calorie deficit produces faster short-term results but is harder to sustain and more likely to include the loss of lean muscle alongside fat. A moderate, sustainable deficit \u2014 paired with adequate protein intake and regular strength training \u2014 tends to preserve more muscle mass and be easier to maintain consistently, which matters more for long-term outcomes than short-term speed.<\/p>\n\n<h2>Factors that affect your calorie needs<\/h2>\n<ul>\n<li><b>Body size and composition<\/b> \u2014 larger bodies and higher muscle mass both increase calorie needs.<\/li>\n<li><b>Activity level<\/b> \u2014 the single biggest lever most people can control day to day, spanning roughly a 1.2x to 1.9x multiplier on BMR.<\/li>\n<li><b>Age<\/b> \u2014 calorie needs generally decline gradually with age, mostly due to natural reductions in muscle mass.<\/li>\n<li><b>Sex<\/b> \u2014 men typically have higher calorie needs than women at the same weight, primarily due to typical differences in muscle mass.<\/li>\n<li><b>Goals<\/b> \u2014 building muscle, losing fat, or maintaining weight each call for a different calorie target relative to your TDEE.<\/li>\n<\/ul>\n\n<h2>Common myths about calories and metabolism<\/h2>\n<p>\"Starvation mode\" from moderate dieting is often exaggerated in popular discussion \u2014 while very prolonged, severe restriction can measurably slow metabolism, typical moderate deficits used for weight management don't cause the dramatic metabolic shutdown often claimed. Similarly, no specific food \"burns fat\" on its own, and skipping meals doesn't reliably boost metabolism \u2014 total daily calorie balance is what matters most, not meal timing or specific \"fat-burning\" foods.<\/p>\n\n<h2>Tips for sustainable nutrition<\/h2>\n<ul>\n<li><b>Prioritize protein<\/b> \u2014 it supports muscle preservation during a deficit and tends to be more satiating than equivalent calories from fat or carbs.<\/li>\n<li><b>Read nutrition labels<\/b> \u2014 serving sizes are often smaller than what people actually eat, which is a common source of underestimating intake.<\/li>\n<li><b>Plan meals ahead<\/b> \u2014 the suggested meal distribution above is a starting point; adjust the split to fit your actual schedule and hunger patterns.<\/li>\n<li><b>Stay hydrated<\/b> \u2014 thirst is sometimes mistaken for hunger, and adequate water intake supports overall health regardless of your calorie goal.<\/li>\n<li><b>Prioritize sleep and stress management<\/b> \u2014 both measurably affect appetite regulation and food choices, often more than people expect.<\/li>\n<\/ul>\n\n<h2>Related health calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/bmr\">BMR Calculator<\/a> for a formula comparison and body-composition-based estimate, the <a href=\"#\/calculator\/bmi\">BMI Calculator<\/a> for a healthy weight range, and the <a href=\"#\/calculator\/body-fat\">Body Fat Calculator<\/a> for a composition-based estimate.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How many calories do I need?<\/summary><p>It depends on your BMR, activity level, and goal \u2014 this calculator combines all three into a personalized daily target, shown alongside your maintenance (TDEE) figure for context.<\/p><\/details>\n<details><summary>How many calories should I eat to lose weight?<\/summary><p>Generally a moderate deficit below your TDEE, commonly in the 250-500 kcal\/day range for gradual, more sustainable loss \u2014 see the goals table above for your specific numbers.<\/p><\/details>\n<details><summary>Is 1,200 calories enough?<\/summary><p>For many adults, 1,200 kcal\/day is quite low and may not meet basic nutritional needs \u2014 targets below roughly 1,200 kcal\/day are flagged in this calculator and should be discussed with a healthcare professional rather than pursued independently.<\/p><\/details>\n<details><summary>What is TDEE?<\/summary><p>Total Daily Energy Expenditure \u2014 your BMR multiplied by an activity factor, representing your actual estimated daily calorie burn including movement and exercise.<\/p><\/details>\n<details><summary>How accurate is this calculator?<\/summary><p>It uses the widely-used Mifflin-St Jeor equation and standard activity multipliers \u2014 reasonably accurate as a starting estimate for most people, though individual metabolism varies and real-world results should be adjusted based on your own tracked progress over time.<\/p><\/details>\n<details><summary>Does age affect calorie needs?<\/summary><p>Yes \u2014 calorie needs generally decline gradually with age, primarily due to natural reductions in muscle mass rather than age itself.<\/p><\/details>\n<details><summary>How do exercise and muscle mass change calorie requirements?<\/summary><p>Both increase calorie needs \u2014 exercise directly through activity burn, and muscle mass indirectly by raising BMR, since muscle tissue burns more at rest than fat tissue.<\/p><\/details>`,\n\n'body-fat':`\n<h2>Body fat calculator \u2014 U.S. Navy and BMI-based methods compared<\/h2>\n<p>This calculator estimates body fat percentage two ways at once: the U.S. Navy circumference method (using neck, waist, and for women, hip measurements) and a BMI-based estimate that works from just height, weight, age and sex. It also breaks your result into fat mass and lean body mass, calculates FFMI, offers a goal body fat planner, and estimates protein and calorie needs based on your lean mass.<\/p>\n\n<h2>What is body fat percentage, and why does it matter more than weight alone?<\/h2>\n<p>Body fat percentage is the proportion of your total body weight that is fat tissue, as opposed to muscle, bone, organs and water. Two people can weigh exactly the same and have very different body fat percentages depending on how much of that weight is lean tissue versus fat \u2014 which is exactly why body composition is often more informative than weight or BMI alone for understanding health and fitness.<\/p>\n\n<h2>The U.S. Navy method<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Men<\/div>\n  <div class=\"f-eq\">BF% = 495 \/ (1.0324 \u2212 0.19077\u00d7log\u2081\u2080(waist\u2212neck) + 0.15456\u00d7log\u2081\u2080(height)) \u2212 450<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Women<\/div>\n  <div class=\"f-eq\">BF% = 495 \/ (1.29579 \u2212 0.35004\u00d7log\u2081\u2080(waist+hip\u2212neck) + 0.22100\u00d7log\u2081\u2080(height)) \u2212 450<\/div>\n<\/div>\n<p>Developed by the U.S. Navy for fitness assessment, this method uses circumference measurements to estimate body fat without calipers or scans. It's generally considered reasonably accurate for most body types when measurements are taken carefully \u2014 measure the neck just below the larynx, the waist at the navel, and hips (for women) at the widest point, using a snug but not tight tape measure.<\/p>\n\n<h2>The BMI-based estimate<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Deurenberg formula<\/div>\n  <div class=\"f-eq\">BF% = 1.20 \u00d7 BMI + 0.23 \u00d7 Age \u2212 10.8 \u00d7 (1 if male, 0 if female) \u2212 5.4<\/div>\n<\/div>\n<p>This method needs no tape measure at all, estimating body fat from BMI, age and sex alone. It's convenient but generally less accurate than the Navy method, since it can't account for body shape \u2014 someone very muscular will often be overestimated, and someone with less muscle mass may be underestimated. Showing both methods side by side, as this calculator does, gives a useful sanity check on either estimate.<\/p>\n\n<h2>BMI vs. body fat percentage<\/h2>\n<p>BMI only considers weight relative to height, treating muscle and fat identically. Body fat percentage directly addresses what BMI cannot: composition. A bodybuilder and a sedentary person of the same height and weight can have the same BMI but dramatically different body fat percentages and health profiles \u2014 this is the core reason body composition metrics have become popular alongside, not instead of, BMI.<\/p>\n\n<h2>Body fat categories<\/h2>\n<p>Category thresholds differ by sex because women naturally carry more essential fat than men, related to hormonal and reproductive function. \"Essential fat\" is the minimum needed for basic physiological health; \"athletes\" and \"fitness\" ranges reflect visibly lean, trained physiques; \"average\" covers most of the general population; and \"high\" is associated with increased health risk in population studies.<\/p>\n\n<h2>Fat mass, lean mass, and FFMI<\/h2>\n<p><b>Fat mass<\/b> is your total weight multiplied by your body fat percentage. <b>Lean body mass<\/b> is everything else \u2014 muscle, bone, organs, water. <b>FFMI<\/b> (Fat-Free Mass Index) normalizes lean mass to height, similar to how BMI normalizes total weight, and is popular among strength athletes for tracking muscle gain independent of body fat changes.<\/p>\n\n<h2>Body recomposition \u2014 losing fat while maintaining muscle<\/h2>\n<p>\"Recomposition\" refers to losing fat while maintaining or even gaining muscle simultaneously \u2014 most achievable with a modest calorie deficit (or maintenance calories), adequate protein intake, and consistent resistance training, particularly for people newer to strength training or returning after a break. It's generally a slower process than pure weight loss, but tends to produce a more favorable body composition outcome than weight loss alone.<\/p>\n\n<h2>Safe, sustainable fat-loss strategies<\/h2>\n<ul>\n<li><b>Prioritize protein<\/b> \u2014 roughly 2.0-2.4g per kg of lean mass is a commonly used range to support muscle preservation during a fat-loss phase.<\/li>\n<li><b>Keep resistance training in the plan<\/b> \u2014 it's the primary signal that tells your body to preserve muscle rather than lose it alongside fat.<\/li>\n<li><b>Favor a moderate deficit<\/b> \u2014 large, aggressive deficits increase the risk of losing lean mass along with fat.<\/li>\n<li><b>Track trends, not single measurements<\/b> \u2014 body fat estimates (from any method) naturally fluctuate day to day; look at the trend over weeks, not one reading.<\/li>\n<\/ul>\n\n<h2>Related health calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/bmi\">BMI Calculator<\/a> for a healthy weight range, the <a href=\"#\/calculator\/bmr\">BMR Calculator<\/a> for formula comparisons, and the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for a full daily nutrition plan built from your energy needs.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is a healthy body fat percentage?<\/summary><p>Roughly 14-24% for men and 21-31% for women is generally considered within the \"fitness\" to \"average\" range, though healthy ranges vary by age, goals and individual factors.<\/p><\/details>\n<details><summary>How accurate is the calculator?<\/summary><p>Both methods are estimates \u2014 the Navy method is generally more accurate than BMI-based estimates but still less precise than clinical methods like DEXA scans or hydrostatic weighing.<\/p><\/details>\n<details><summary>Is the U.S. Navy method reliable?<\/summary><p>It's a reasonably reliable field method when measurements are taken carefully and consistently, making it popular for tracking trends over time even if any single reading has some margin of error.<\/p><\/details>\n<details><summary>Can I reduce body fat without losing muscle?<\/summary><p>Yes, to a meaningful degree \u2014 adequate protein intake and consistent resistance training during a moderate calorie deficit are the main levers for preserving muscle while losing fat.<\/p><\/details>\n<details><summary>What's the difference between BMI and body fat percentage?<\/summary><p>BMI only relates weight to height and can't distinguish muscle from fat; body fat percentage directly measures body composition, which is why the two can tell very different stories for the same person.<\/p><\/details>\n<details><summary>Does age affect body fat percentage?<\/summary><p>Yes \u2014 body fat percentage tends to rise gradually with age even at a stable weight, largely due to natural age-related muscle loss unless offset by resistance training.<\/p><\/details>`,\n\n\n'ideal-weight':`\n<h2>Ideal weight calculator \u2014 six formulas, one honest answer: there's a range, not a number<\/h2>\n<p>Most ideal weight calculators show a single value from a single formula. This one deliberately shows six established formulas side by side, averages them, cross-references your BMI-based healthy range, and adjusts for body frame size \u2014 because the honest answer to \"what's my ideal weight\" is that no single formula is definitively correct. They were developed decades apart, for different purposes, and reasonably disagree by several kilograms.<\/p>\n\n<h2>What does \"ideal weight\" actually mean?<\/h2>\n<p>\"Ideal body weight\" formulas were originally developed mostly for clinical purposes \u2014 estimating medication dosages, for instance \u2014 not as a personal fitness target. They're population-level statistical estimates based on height alone, and were never designed to account for muscle mass, bone density, frame size or individual body composition. Treating any single formula's output as a precise personal target overstates what these formulas were built to do.<\/p>\n\n<h2>Why different formulas give different answers<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Formula<\/th><th>Origin<\/th><th>Notes<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Devine (1974)<\/td><td>Originally for drug dosing<\/td><td>The most widely cited and used as a clinical reference<\/td><\/tr>\n    <tr><td>Robinson (1983)<\/td><td>Refinement of Devine<\/td><td>Tends to estimate slightly lower than Devine<\/td><\/tr>\n    <tr><td>Miller (1983)<\/td><td>Refinement of Devine<\/td><td>Tends to estimate slightly higher than Robinson<\/td><\/tr>\n    <tr><td>Hamwi (1964)<\/td><td>Quick clinical estimate<\/td><td>One of the oldest, still commonly cited<\/td><\/tr>\n    <tr><td>Lorentz (1929)<\/td><td>Early European formula<\/td><td>Sensitive to height in a different way than the others<\/td><\/tr>\n    <tr><td>Peterson (2016)<\/td><td>Modern BMI-based revision<\/td><td>Designed to better match a target BMI of 22 across height ranges<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>Because each formula was derived from different reference populations and eras, they naturally diverge \u2014 usually by a few kilograms, sometimes more at height extremes. Averaging them, as this calculator does, gives a more balanced reference point than trusting any single formula alone.<\/p>\n\n<h2>Frame size \u2014 why it matters<\/h2>\n<p>Two people of identical height can have meaningfully different bone structure and frame size, which affects how much muscle and skeletal mass they naturally carry. This calculator applies a simple \u00b110% adjustment for small and large frames relative to medium, which is a common simplified approach \u2014 for a precise frame size assessment, wrist circumference relative to height is sometimes used clinically, but the adjustment here is intended as a reasonable approximation rather than a clinical measurement.<\/p>\n\n<h2>Ideal weight vs. healthy weight vs. BMI range<\/h2>\n<p>These are related but distinct concepts. <b>Ideal weight formulas<\/b> give a single height-based estimate. <b>Healthy BMI range<\/b> (18.5\u201324.9) gives a wider, evidence-based weight span for your height. <b>Body composition<\/b> (muscle vs. fat) explains why two people at the same \"ideal weight\" can look and function very differently. This calculator shows all three together deliberately, since relying on just one paints an incomplete picture.<\/p>\n\n<h2>BMI's limitations, briefly<\/h2>\n<p>BMI and BMI-derived ideal weight formulas can't distinguish muscle from fat. A muscular, athletic person will often be told their \"ideal weight\" is well below their actual healthy weight, simply because the formula has no way to account for higher-than-average muscle mass. If this applies to you, body fat percentage and how you actually look and perform are more informative than any formula on this page.<\/p>\n\n<h2>Safe, sustainable approaches to reaching a weight goal<\/h2>\n<ul>\n<li><b>Treat the target as a range, not a precise number<\/b> \u2014 anywhere within your healthy BMI range is a reasonable goal, not just one specific formula's output.<\/li>\n<li><b>Favor a moderate pace<\/b> \u2014 the goal timeline above uses a ~500 cal\/day deficit as a common, relatively sustainable reference pace.<\/li>\n<li><b>Combine weight goals with strength training<\/b> \u2014 preserving or building muscle changes body composition in ways the scale alone won't show.<\/li>\n<li><b>Reassess periodically<\/b> \u2014 as body composition changes, what's \"ideal\" for you may shift too.<\/li>\n<\/ul>\n\n<h2>Related health calculators<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/bmi\">BMI Calculator<\/a> for category classification, the <a href=\"#\/calculator\/body-fat\">Body Fat Calculator<\/a> for composition, the <a href=\"#\/calculator\/bmr\">BMR Calculator<\/a> for metabolism, and the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for a complete nutrition plan.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>What is an ideal weight?<\/summary><p>A height-based statistical estimate of a \"reference\" body weight, originally developed mostly for clinical use \u2014 best treated as a rough guide rather than a precise personal target.<\/p><\/details>\n<details><summary>Which ideal weight formula is most accurate?<\/summary><p>None is definitively \"most accurate\" for individuals \u2014 they're all population-level estimates. Averaging several, as this calculator does, tends to be more balanced than relying on just one.<\/p><\/details>\n<details><summary>Is BMI the same as ideal weight?<\/summary><p>No \u2014 BMI is a ratio of weight to height used to define a healthy range; ideal weight formulas estimate a single reference point, often derived differently from BMI.<\/p><\/details>\n<details><summary>Why do formulas give different answers?<\/summary><p>They were developed independently, often decades apart, using different reference populations and methods \u2014 disagreement of several kilograms between formulas is normal and expected.<\/p><\/details>\n<details><summary>Does age affect ideal weight?<\/summary><p>Most classic formulas don't directly factor in age, though body composition (and therefore what's genuinely healthy) does shift with age, mainly through natural muscle loss over time.<\/p><\/details>\n<details><summary>Does muscle mass affect ideal weight?<\/summary><p>Significantly \u2014 none of these formulas account for muscle mass, so muscular individuals are often \"underestimated\" by every formula on this page.<\/p><\/details>\n<details><summary>Should I aim for the exact calculated weight?<\/summary><p>Not necessarily \u2014 treating the healthy BMI range as your target, rather than one formula's exact output, is generally more realistic and sustainable.<\/p><\/details>`,\n\npregnancy:`\n<p style=\"background:#FFF4E5;border:1px solid #F5C97A;border-radius:10px;padding:14px 16px;font-size:.88rem;color:#7A5A1E;\"><b>Medical disclaimer:<\/b> This calculator and the information below provide general estimates for educational purposes only. They are not medical advice and do not replace care from a qualified healthcare provider. Always follow your own doctor or midwife's guidance for your specific pregnancy.<\/p>\n\n<h2>Pregnancy due date calculator \u2014 four calculation methods explained<\/h2>\n<p>Most due date calculators only accept your last menstrual period. This one supports four established methods \u2014 LMP, conception date, IVF transfer date, and ultrasound dating \u2014 because different people have different information available, and some methods are more precise than others depending on your situation.<\/p>\n\n<h2>How due dates are estimated<\/h2>\n<div class=\"formula-box\">\n  <div class=\"f-label\">LMP method (Naegele's rule)<\/div>\n  <div class=\"f-eq\">Due date = LMP + 280 days, adjusted for cycle length<\/div>\n<\/div>\n<p>The traditional method assumes a 28-day cycle with ovulation around day 14. This calculator adjusts for your actual average cycle length, since a longer or shorter cycle shifts ovulation \u2014 and therefore conception \u2014 earlier or later relative to the LMP.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Conception date method<\/div>\n  <div class=\"f-eq\">Due date = Conception date + 266 days (38 weeks)<\/div>\n<\/div>\n<p>If you know your approximate conception date more precisely than your LMP (for example, from ovulation tracking), this method skips the cycle-length assumption entirely.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">IVF transfer date method<\/div>\n  <div class=\"f-eq\">Due date = Transfer date + 261 days (5-day transfer) or + 263 days (3-day transfer)<\/div>\n<\/div>\n<p>IVF pregnancies have a precisely known embryo age at transfer, making this typically the most accurate dating method available when applicable.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Ultrasound dating method<\/div>\n  <div class=\"f-eq\">Due date = Ultrasound date \u2212 gestational age at ultrasound + 280 days<\/div>\n<\/div>\n<p>Early ultrasound measurements (particularly in the first trimester) are often considered a reliable dating method, sometimes more so than LMP-based dating if cycles are irregular or the LMP date is uncertain.<\/p>\n\n<h2>Gestational age vs. fetal age \u2014 a common source of confusion<\/h2>\n<p><b>Gestational age<\/b> is counted from the first day of the last menstrual period \u2014 the standard used throughout pregnancy care, including on this calculator. <b>Fetal age<\/b> (or \"conceptional age\") is counted from the estimated date of conception, roughly two weeks later. So \"6 weeks pregnant\" (gestational age) generally corresponds to a fetus that has been developing for about 4 weeks since conception. Nearly all clinical dating, due dates, and the weekly milestones referenced here use gestational age.<\/p>\n\n<h2>Why your due date might change<\/h2>\n<p>It's common for an early ultrasound to shift the estimated due date by several days from an LMP-based estimate \u2014 this isn't unusual or concerning. Ultrasound measurements in the first trimester are generally considered quite reliable for dating, and healthcare providers often use the ultrasound date going forward once it's available, particularly if it differs meaningfully from the LMP-based estimate.<\/p>\n\n<h2>Trimester overview<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Trimester<\/th><th>Weeks<\/th><th>General overview<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>First<\/td><td>1\u201313<\/td><td>Early development; many people experience fatigue and nausea, though experiences vary widely<\/td><\/tr>\n    <tr><td>Second<\/td><td>14\u201327<\/td><td>Often described as more comfortable for many; common screenings like the anatomy scan typically occur in this window<\/td><\/tr>\n    <tr><td>Third<\/td><td>28\u201340+<\/td><td>Continued growth; more frequent prenatal visits are typical as the due date approaches<\/td><\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>Common symptoms by stage (general information only)<\/h2>\n<p>Symptoms vary enormously between individuals and pregnancies \u2014 the following are commonly reported, not universal or diagnostic. Early pregnancy is often associated with fatigue, nausea, and breast tenderness. The second trimester is often reported as more comfortable, though back pain and round ligament discomfort are common. The third trimester often brings physical discomfort as space becomes limited, along with Braxton Hicks contractions (irregular practice contractions) for many people. Any concerning, severe, or unusual symptoms at any stage should be discussed with a healthcare provider promptly rather than self-diagnosed from general information like this.<\/p>\n\n<h2>Prenatal care basics<\/h2>\n<p>Regular prenatal visits allow a healthcare provider to monitor both parent and baby throughout pregnancy, typically becoming more frequent as the due date approaches. Common elements of prenatal care include blood pressure and weight checks, screening tests at various stages (such as the first-trimester nuchal translucency screening and second-trimester anatomy scan referenced in the milestone table above), and ongoing discussion of any symptoms or concerns. The specific schedule and tests recommended vary by provider, individual health history, and region \u2014 the general windows shown by this calculator are for orientation only, not a substitute for your provider's personalized schedule.<\/p>\n\n<h2>Nutrition, exercise and sleep during pregnancy<\/h2>\n<p>General guidance commonly discussed with healthcare providers includes adequate hydration, a varied diet with sufficient protein and key nutrients (such as folate and iron), and continuing safe physical activity as advised by a provider. Sleep can become more difficult in later pregnancy due to physical discomfort, which is a common and normal experience. Specific nutrition, supplement, and exercise recommendations should come from your own healthcare provider, since individual needs vary significantly. See the <a href=\"#\/calculator\/calorie\">Calorie Calculator<\/a> for general nutrition planning and the <a href=\"#\/calculator\/bmi\">BMI Calculator<\/a> for pre-pregnancy BMI context.<\/p>\n\n<h2>Fetal size comparisons \u2014 where do these come from?<\/h2>\n<p>Week-by-week fruit and object comparisons (like \"the size of a lemon\") are a widely used, informal way to visualize approximate fetal size \u2014 they're illustrative, not precise measurements, and actual size varies between pregnancies. They're included here because they're genuinely popular and help make abstract measurements more relatable, not because they carry clinical significance.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How accurate is the due date calculator?<\/summary><p>Due dates from any method are estimates \u2014 only a small percentage of babies are born on their exact calculated due date. Ultrasound dating in early pregnancy is generally considered among the more reliable estimation methods.<\/p><\/details>\n<details><summary>Why did my ultrasound due date change from my LMP-based date?<\/summary><p>This is common and not a cause for concern by itself \u2014 early ultrasound measurements are often used to refine or adjust the estimated due date, particularly if cycles are irregular.<\/p><\/details>\n<details><summary>What if I have irregular menstrual cycles?<\/summary><p>LMP-based dating becomes less reliable with irregular cycles \u2014 conception date or early ultrasound dating (if available) are generally more accurate in that situation.<\/p><\/details>\n<details><summary>What if I conceived through IVF?<\/summary><p>IVF pregnancies have a precisely known embryo transfer date and embryo age, generally making the IVF dating method the most precise option available \u2014 select it above and enter your transfer date and embryo age.<\/p><\/details>\n<details><summary>Can babies arrive before or after the due date?<\/summary><p>Yes, routinely \u2014 full-term is generally considered a range (around weeks 37\u201342), not a single date, and birth timing naturally varies.<\/p><\/details>\n<details><summary>How is gestational age calculated?<\/summary><p>Gestational age is counted from the first day of the last menstrual period, which is roughly two weeks before actual conception \u2014 this is the standard used throughout pregnancy care.<\/p><\/details>\n<details><summary>What happens after 40 weeks?<\/summary><p>Many pregnancies continue past 40 weeks without concern; your healthcare provider will typically discuss monitoring or timing considerations with you individually as your specific situation warrants.<\/p><\/details>`,\n\nage:`\n<h2>How old am I? How this Age Calculator works<\/h2>\n<p>This tool answers \"how old am I\" down to the exact day \u2014 and goes further, calculating your age in weeks, hours, minutes and seconds, live and continuously updating. It also works as an age calculator from date of birth for a specific past or future date, and as a two-person age-difference calculator.<\/p>\n<div class=\"formula-box\">\n  <div class=\"f-label\">How the exact age is calculated<\/div>\n  <div class=\"f-eq\">Years, months, days = calendar difference between DOB and target date<\/div>\n<\/div>\n<p>Rather than dividing total days by 365.25 (which drifts over time), this calculator walks the actual calendar month by month, so every figure \u2014 including the years\/months\/days breakdown \u2014 is exact, and automatically accounts for leap years and different month lengths.<\/p>\n\n<h2>Leap year handling<\/h2>\n<p>A leap year adds a 366th day (February 29) roughly every four years, to keep the calendar aligned with Earth's orbit. This calculator specifically checks how many February 29ths have actually occurred between your date of birth and the target date \u2014 not just how many leap years technically exist in that span \u2014 so the \"leap years experienced\" figure and the exact day\/week\/hour counts are always accurate, even if you were born on or near a leap day.<\/p>\n\n<h2>What's included beyond the basic age<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Feature<\/th><th>What it tells you<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Live counter<\/td><td>Weeks, days, hours, minutes and seconds alive, updating in real time<\/td><\/tr>\n    <tr><td>Zodiac &amp; generation<\/td><td>Western zodiac sign, Chinese zodiac animal, and generational cohort (e.g. Millennial, Gen Z)<\/td><\/tr>\n    <tr><td>Life progress<\/td><td>A visual bar comparing your age to a 73-year global average lifespan<\/td><\/tr>\n    <tr><td>Body estimates<\/td><td>Approximate heartbeats, breaths taken, and time spent asleep so far<\/td><\/tr>\n    <tr><td>Historical tallies<\/td><td>How many FIFA World Cups, Summer Olympic Games, leap years and full moons have occurred in your lifetime<\/td><\/tr>\n    <tr><td>Planetary ages<\/td><td>Your age converted into Mercury, Venus, Mars, Jupiter, Saturn, Uranus and Neptune years, based on each planet's real orbital period<\/td><\/tr>\n    <tr><td>Milestone tracker<\/td><td>Common age-based milestones (16, 18, 21, 25, 30, 35, 50, 62, 65, 67, 100) with reached\/upcoming status<\/td><\/tr>\n    <tr><td>Special day counts<\/td><td>Your 10,000th and 20,000th day alive, the day you turned (or will turn) 1 billion seconds old, and your half-birthday<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>Milestone ages shown (drinking age, Social Security, Medicare, etc.) reflect common US benchmarks since that's this calculator's largest audience \u2014 the underlying age-difference math works the same everywhere, but eligibility ages for voting, driving, or retirement do vary by country.<\/p>\n\n<h2>Age on a specific date &amp; comparing two people<\/h2>\n<p>Switch to \"Age on a date\" to answer questions like \"how old will I be on a future date\" or \"how old was I on a specific past date\" \u2014 useful for planning milestone birthdays, checking eligibility cutoffs, or historical curiosity. Switch to \"Compare two people\" to instantly see the age gap between any two dates of birth, along with both people's current ages.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How is chronological age calculated?<\/summary><p>Chronological age is the exact time elapsed between a date of birth and a reference date, expressed in calendar years, months and days \u2014 exactly what this calculator computes, down to the second if you want it.<\/p><\/details>\n<details><summary>Does a leap year affect my age?<\/summary><p>Only in the rare case where someone born on February 29 has their birthday fall on a non-leap year \u2014 this calculator treats that birthday as March 1 in non-leap years so the age calculation stays consistent.<\/p><\/details>\n<details><summary>How many days old am I?<\/summary><p>Your total days alive is shown in the live counter section, updating automatically, alongside the equivalent number of weeks, hours, minutes and seconds.<\/p><\/details>\n<details><summary>Can I calculate someone else's age, or a future age?<\/summary><p>Yes \u2014 use \"Age on a date\" for any past or future reference date, or \"Compare two people\" to find the exact age difference between two dates of birth.<\/p><\/details>\n<details><summary>Why 73 years for the life-progress bar?<\/summary><p>73 years is roughly the current global average life expectancy. It's shown purely as an illustrative benchmark, not a prediction \u2014 individual life expectancy depends on many personal and regional factors.<\/p><\/details>`,\n\n'date-difference':`\n<h2>Date difference calculator \u2014 days between two dates, business days, and more<\/h2>\n<p>This is a complete date and time calculation hub, not just a single-purpose tool. Beyond the exact difference between two dates, it handles business-day counting with optional public holidays, date addition and subtraction, recurring date schedules, and live countdowns \u2014 the same underlying calendar math applied to several real everyday questions: calculating days between dates, tracking work experience, planning a project timeline, or counting down to an event.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Core calculation<\/div>\n  <div class=\"f-eq\">Total days = (End date \u2212 Start date) in days, \u00b1 1 if counting inclusively<\/div>\n<\/div>\n\n<h2>Inclusive vs. exclusive counting \u2014 the detail most calculators skip<\/h2>\n<p>This is the single most common source of \"why do two calculators give different answers\" confusion. <b>Exclusive counting<\/b> measures the gap between two dates \u2014 January 1 to January 5 is 4 days apart. <b>Inclusive counting<\/b> counts both endpoints as part of the span \u2014 January 1 to January 5 inclusive is 5 days, because it includes both the 1st and the 5th. Inclusive counting is typically what you want for questions like \"how many days is my vacation\" or \"how many days is this rental booked,\" where both the start and end day are actually used. Exclusive counting is typically correct for measuring elapsed time or scheduling gaps. This calculator lets you toggle between the two explicitly, so the number always matches what you actually meant to ask.<\/p>\n\n<h2>How leap years are handled<\/h2>\n<p>A leap year adds February 29, occurring in years divisible by 4, except century years not divisible by 400 (so 2000 was a leap year, but 1900 was not). Rather than approximating with 365.25 days per year, this calculator counts the actual calendar days between your two dates, which automatically absorbs every leap day correctly \u2014 and separately reports how many leap years your selected range crosses, since that's often a useful fact on its own (for insurance terms, age calculations, or long-term planning).<\/p>\n\n<h2>Business days vs. working days vs. calendar days<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Term<\/th><th>What it means here<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Calendar days<\/td><td>Every day in the range, including weekends and holidays<\/td><\/tr>\n    <tr><td>Weekend days<\/td><td>Days falling on whichever days you define as the weekend (Saturday\/Sunday by default \u2014 adjustable, since some regions use Friday\/Saturday)<\/td><\/tr>\n    <tr><td>Business days<\/td><td>Calendar days minus weekend days minus any selected public holidays<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>Public holiday sets are provided for the US, UK, Canada, Australia, Pakistan and India, covering major fixed-date and common floating holidays (like Easter-based and \"Nth weekday of month\" holidays, calculated directly rather than hardcoded). This is intentionally a simplified, illustrative set \u2014 it does not cover regional holidays, one-off government-declared days off, or lunar-calendar holidays (such as Eid or Diwali, whose Gregorian dates shift each year and require religious\/astronomical calendars beyond what a calculator can compute from a formula). For payroll, legal, or contractual deadlines, always confirm against an official regional holiday calendar.<\/p>\n\n<h2>ISO week numbers<\/h2>\n<p>The ISO 8601 standard defines week 1 of a year as the week containing that year's first Thursday, with weeks running Monday to Sunday. This differs from simply dividing days by 7, which is why two different \"week number\" calculators can disagree near year boundaries. This calculator reports total elapsed weeks as a simple duration (total days \u00f7 7), which is what most people mean when asking \"how many weeks between these dates\" \u2014 for calendar week numbering specifically, ISO week rules would apply instead.<\/p>\n\n<h2>Common mistakes when calculating date differences<\/h2>\n<ul>\n<li><b>Forgetting inclusive\/exclusive intent<\/b> \u2014 leads to answers that are off by exactly one day.<\/li>\n<li><b>Using 30 or 30.44 days per month<\/b> for exact month counts \u2014 this works for rough estimates but not for a precise \"years, months, days\" breakdown, which this calculator computes using actual calendar arithmetic instead.<\/li>\n<li><b>Ignoring time zones<\/b> when start and end include specific times \u2014 this calculator uses your browser's local time consistently for both ends, which is correct for same-location calculations but worth knowing if you're coordinating across regions.<\/li>\n<li><b>Assuming all public holidays are fixed dates<\/b> \u2014 many are not (Easter, Thanksgiving, and various \"Nth Monday\" holidays shift every year), which is why this calculator computes them algorithmically rather than using a static list.<\/li>\n<\/ul>\n\n<h2>Related tools<\/h2>\n<p>This calculator pairs naturally with the <a href=\"#\/calculator\/age\">Age Calculator<\/a> for birthdate-based questions, the <a href=\"#\/calculator\/retirement\">Retirement Calculator<\/a> and <a href=\"#\/calculator\/mortgage\">Mortgage Calculator<\/a> for long-horizon financial timelines, and the <a href=\"#\/calculator\/pregnancy\">Pregnancy Due Date Calculator<\/a> for date-driven milestones.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I calculate the number of days between two dates?<\/summary><p>Enter a start and end date above and choose inclusive or exclusive counting depending on whether both endpoints should count as full days.<\/p><\/details>\n<details><summary>Does this calculator include leap years?<\/summary><p>Yes \u2014 the day, week, and year\/month\/day calculations all use real calendar arithmetic, so every leap day in range is automatically accounted for, and the number of leap years crossed is shown separately.<\/p><\/details>\n<details><summary>What are business days?<\/summary><p>Business days are calendar days excluding weekends (customizable) and, optionally, public holidays for a selected country.<\/p><\/details>\n<details><summary>What is inclusive counting?<\/summary><p>Inclusive counting treats both the start and end date as full days within the span, adding one extra day compared to a simple subtraction (exclusive counting).<\/p><\/details>\n<details><summary>Can I exclude weekends?<\/summary><p>Yes \u2014 business-day mode excludes weekends by default, with an adjustable weekend definition, plus optional public holidays for several countries.<\/p><\/details>\n<details><summary>Can I calculate hours or minutes between two dates?<\/summary><p>Yes \u2014 add optional start and end times for a precise breakdown down to hours, minutes and seconds, not just whole days.<\/p><\/details>\n<details><summary>Can I calculate months exactly, not just estimated?<\/summary><p>Yes \u2014 the years\/months\/days readout walks the actual calendar month by month rather than dividing by an average month length, so it's exact regardless of varying month lengths.<\/p><\/details>\n<details><summary>Why do some calculators give different answers for the same two dates?<\/summary><p>Almost always due to inclusive vs. exclusive counting, or one tool using approximate 30-day months while another uses exact calendar arithmetic \u2014 both are \"correct\" for different questions, which is why this calculator makes the counting mode an explicit, visible choice.<\/p><\/details>`,\n\ngpa:`\n<h2>GPA calculator \u2014 a complete academic success hub<\/h2>\n<p>This tool goes beyond a single GPA number. It supports multiple grading systems used around the world, tracks semester GPA alongside cumulative GPA (CGPA), plans the grades you'll need in future semesters to hit a target, analyzes credit hours by category, charts your GPA trend across semesters, predicts academic honors eligibility, and checks scholarship GPA requirements \u2014 all from the same course list.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Core formula<\/div>\n  <div class=\"f-eq\">GPA = \u03a3(Grade points \u00d7 Credit hours) \u00f7 \u03a3(Credit hours)<\/div>\n<\/div>\n<p>Each course's letter grade (or percentage) converts to a grade point on your selected scale, gets multiplied by that course's credit hours, and the sum is divided by total credit hours \u2014 so a 4-credit course affects your GPA twice as much as a 2-credit course.<\/p>\n\n<h2>GPA vs. CGPA<\/h2>\n<p><b>GPA<\/b> (Grade Point Average) usually refers to a single semester's average. <b>CGPA<\/b> (Cumulative GPA) covers your entire academic record to date. This calculator computes both: enter your previous CGPA and previous total credits, and it combines them with your current semester's courses using a credit-weighted average \u2014 so a strong or weak semester influences your CGPA proportionally to how many credits it represents relative to your whole academic history.<\/p>\n\n<h2>Weighted vs. unweighted GPA<\/h2>\n<p>An <b>unweighted GPA<\/b> treats every course the same regardless of difficulty (an A is 4.0 whether it's a regular or an honors class). A <b>weighted GPA<\/b> gives extra points for honors, AP, or IB courses (an A might be worth 5.0 in an AP class). This calculator uses unweighted, credit-hour-weighted averaging by default across all supported grading systems \u2014 if your school uses a weighted scale for advanced courses, you can approximate it by entering the school-provided grade point directly where the system allows numeric entry.<\/p>\n\n<h2>Grading systems supported<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>System<\/th><th>Scale<\/th><th>Typical region<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>US 4.0<\/td><td>0\u20134.0<\/td><td>United States, Pakistan (HEC)<\/td><\/tr>\n    <tr><td>US 4.3<\/td><td>0\u20134.3<\/td><td>Some US &amp; Canadian institutions<\/td><\/tr>\n    <tr><td>10-Point CGPA<\/td><td>0\u201310<\/td><td>India, some Pakistani universities<\/td><\/tr>\n    <tr><td>Australia 7-Point<\/td><td>0\u20137<\/td><td>Australian universities<\/td><\/tr>\n    <tr><td>Gulf 5.0<\/td><td>0\u20135<\/td><td>Saudi Arabia, UAE<\/td><\/tr>\n    <tr><td>Germany 1.0\u20135.0<\/td><td>1 (best) \u2013 5 (fail)<\/td><td>Germany \u2014 note this scale is inverted, lower is better<\/td><\/tr>\n    <tr><td>Percentage<\/td><td>0\u2013100%<\/td><td>UK and others using percentage\/degree classification<\/td><\/tr>\n  <\/tbody>\n<\/table>\n<p>Country presets set a sensible default, but grading systems vary by individual institution \u2014 always confirm your specific university's official scale for anything that matters academically (transcripts, transfer credit, scholarships).<\/p>\n\n<h2>How repeated courses affect GPA<\/h2>\n<p>Many universities apply \"grade replacement,\" where retaking a failed or low-graded course replaces the old grade in your GPA calculation rather than averaging both attempts. This calculator lets you flag a course as repeated for your own tracking and credit analysis, but since it doesn't store prior failed attempts, the actual grade-replacement math should follow your institution's specific policy \u2014 some average both attempts, others use only the highest, others only the most recent.<\/p>\n\n<h2>Common GPA calculation mistakes<\/h2>\n<ul>\n<li><b>Forgetting to weight by credit hours<\/b> \u2014 a simple average of grade points (ignoring credits) overstates the impact of low-credit courses and understates high-credit ones.<\/li>\n<li><b>Including Pass\/Fail courses in the GPA math<\/b> \u2014 most institutions exclude P\/F courses from GPA entirely, counting them only toward earned credits if passed. This calculator does the same when you mark a course as Pass\/Fail.<\/li>\n<li><b>Mixing grading scales<\/b> \u2014 combining a 4.0-scale previous CGPA with a 10-point current semester without converting first will produce a meaningless number; keep your previous CGPA input on the same scale as your current selection.<\/li>\n<li><b>Ignoring failed courses<\/b> \u2014 a failing grade still carries its full (zero or near-zero) grade points and credit weight in most systems, dragging down the average even though no credit is earned.<\/li>\n<\/ul>\n\n<h2>Related tools<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/grade\">Grade Calculator<\/a> for a single course's required final-exam score, the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for quick percentage conversions, the <a href=\"#\/calculator\/age\">Age Calculator<\/a> and <a href=\"#\/calculator\/date-difference\">Date Difference Calculator<\/a> for academic timeline planning, and the <a href=\"#\/calculator\/loan\">Loan Calculator<\/a> for education financing.<\/p>\n\n<h2>Student success tips<\/h2>\n<ul>\n<li><b>Prioritize high-credit courses<\/b> \u2014 a 4-credit course affects your GPA twice as much as a 2-credit one, so focus extra study time accordingly.<\/li>\n<li><b>Use the target GPA planner early<\/b> \u2014 knowing the average you need in remaining credits well before finals gives you time to actually hit it.<\/li>\n<li><b>Choose electives strategically<\/b> \u2014 if you're close to an honors threshold, an elective in a subject you're confident in can matter more than it seems.<\/li>\n<li><b>Address a bad grade immediately<\/b> \u2014 the earlier you know you need a strong final stretch, the more realistic it is to achieve.<\/li>\n<\/ul>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I calculate GPA?<\/summary><p>Multiply each course's grade point by its credit hours, sum those products across all courses, then divide by the total credit hours.<\/p><\/details>\n<details><summary>What is CGPA?<\/summary><p>Cumulative GPA \u2014 your grade point average across your entire academic record to date, rather than just the current semester.<\/p><\/details>\n<details><summary>What is a good GPA?<\/summary><p>It depends heavily on context (institution, program, country), but on a 4.0 scale, 3.5+ is generally considered strong, 3.0+ solid, and below 2.0 often signals academic risk at many institutions.<\/p><\/details>\n<details><summary>How are credit hours used?<\/summary><p>Credit hours weight each course's contribution to your GPA \u2014 higher-credit courses (like labs or major requirements) influence the average more than lower-credit ones.<\/p><\/details>\n<details><summary>What if I repeat a course?<\/summary><p>Policies vary by institution \u2014 some replace the old grade entirely, others average both attempts. Check your school's specific grade-replacement policy.<\/p><\/details>\n<details><summary>Does GPA include failed courses?<\/summary><p>Generally yes \u2014 a failing grade still carries its grade-point value (typically 0) and credit weight in the GPA calculation, even though no credit is earned toward graduation.<\/p><\/details>\n<details><summary>What is weighted GPA?<\/summary><p>A GPA calculation that gives extra grade points for advanced courses like AP, IB or honors classes, as opposed to an unweighted GPA where every course counts equally regardless of difficulty.<\/p><\/details>\n<details><summary>Can I calculate GPA for multiple semesters?<\/summary><p>Yes \u2014 add a semester label to each course and the calculator will chart your GPA trend semester by semester, in addition to your overall cumulative GPA.<\/p><\/details>`,\n\ngrade:`\n<h2>Grade calculator \u2014 a complete grade planning tool<\/h2>\n<p>This calculator handles both weighted-percentage grading and points-based grading, converts your result into a letter grade and GPA equivalent across several common grading systems, plans exactly what you need on remaining work to hit a target, and lets you test \"what-if\" scenarios before you've even taken the assessment.<\/p>\n\n<div class=\"formula-box\">\n  <div class=\"f-label\">Weighted grade formula<\/div>\n  <div class=\"f-eq\">Current grade = \u03a3(Weight \u00d7 Score) \u00f7 \u03a3(Weight of completed work)<\/div>\n<\/div>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Points-based formula<\/div>\n  <div class=\"f-eq\">Current grade = \u03a3(Points earned) \u00f7 \u03a3(Points possible so far)<\/div>\n<\/div>\n\n<h2>Weighted vs. points-based grading<\/h2>\n<p><b>Weighted grading<\/b> assigns each category (homework, midterms, finals) a fixed percentage of the final grade regardless of how many points each assessment is worth internally \u2014 a final exam worth 40% counts for 40% of your grade whether it's graded out of 100 or 300 points. <b>Points-based grading<\/b> has no separate weight at all: every point counts equally, so a 300-point final naturally carries more weight than a 20-point quiz simply because it has more points available. Both are common; this calculator supports either, and automatically normalizes weights that don't add up to exactly 100%, so partial entry doesn't break the math.<\/p>\n\n<h2>How letter grades and GPA are determined<\/h2>\n<p>Once your percentage is calculated, it's mapped to a letter grade using threshold bands that vary by grading system \u2014 the five presets here (US with +\/-, simple US A-F, Pakistan HEC, UK degree classification, and Australia's HD\/D\/C\/P\/F scale) reflect commonly used cutoffs, then convert to an approximate GPA equivalent. Always confirm your specific institution's exact thresholds for anything that affects your official transcript.<\/p>\n\n<h2>Common grading mistakes<\/h2>\n<ul>\n<li><b>Averaging scores without weighting<\/b> \u2014 a simple average of assignment percentages ignores that a final exam usually counts far more than a single homework set.<\/li>\n<li><b>Forgetting ungraded work<\/b> \u2014 your \"current grade\" only reflects completed assessments; it isn't your final grade until everything is graded, which is why the goal planner separately projects best-case and worst-case outcomes.<\/li>\n<li><b>Confusing points possible with percentage<\/b> \u2014 a 45\/50 quiz (90%) and a 45\/100 quiz are very different results, even though \"45\" looks the same on paper.<\/li>\n<li><b>Assuming a target is always achievable<\/b> \u2014 if the required score on remaining work exceeds 100%, the target genuinely is not reachable without extra credit or grade forgiveness policies.<\/li>\n<\/ul>\n\n<h2>Tips to improve your grade efficiently<\/h2>\n<ul>\n<li><b>Focus on high-weight assessments first<\/b> \u2014 improving a 5%-weight quiz by 20 points helps far less than improving a 40%-weight final by the same margin.<\/li>\n<li><b>Use the what-if simulator before exams<\/b> \u2014 knowing exactly what score keeps you in your target letter-grade band focuses your study time realistically.<\/li>\n<li><b>Recalculate after every graded assessment<\/b> \u2014 your required average on remaining work shifts every time a new grade comes in, sometimes significantly.<\/li>\n<li><b>Talk to your instructor early<\/b> if the math shows your target is mathematically out of reach \u2014 extra credit or grade adjustments are a conversation for before finals week, not after.<\/li>\n<\/ul>\n\n<h2>Related tools<\/h2>\n<p>Pair this with the <a href=\"#\/calculator\/gpa\">GPA Calculator<\/a> for multi-course cumulative tracking, the <a href=\"#\/calculator\/percentage\">Percentage Calculator<\/a> for quick conversions, and the <a href=\"#\/calculator\/age\">Age Calculator<\/a> or <a href=\"#\/calculator\/date-difference\">Date Difference Calculator<\/a> for academic scheduling.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I calculate my final grade?<\/summary><p>Enter each assessment's weight (or points) and score, and the calculator computes a weighted average of everything completed so far.<\/p><\/details>\n<details><summary>What is a weighted grade?<\/summary><p>A grade where each category (homework, exams, projects) contributes a fixed percentage to your final grade, regardless of how many individual points that category is graded out of.<\/p><\/details>\n<details><summary>How are letter grades assigned?<\/summary><p>Your final percentage is compared against threshold bands (e.g., 90%+ = A) that vary by grading system \u2014 select your institution's system for the closest match.<\/p><\/details>\n<details><summary>How do percentages convert to GPA?<\/summary><p>Each letter-grade band maps to an approximate GPA value (e.g., A = 4.0) based on the grading system you select; exact GPA conversion tables vary by school.<\/p><\/details>\n<details><summary>What if my teacher uses points instead of percentages?<\/summary><p>Switch to Points-based mode and enter obtained\/total points directly for each assessment \u2014 no manual percentage conversion needed.<\/p><\/details>\n<details><summary>Can I predict my final grade before all assessments are graded?<\/summary><p>Yes \u2014 the goal planner shows what you need on remaining work for a target grade, and the pass\/fail predictor projects a likely outcome assuming your average continues.<\/p><\/details>`,\n\npassword:`\n<h2>Strong password generator \u2014 how it works<\/h2>\n<p>This free password generator creates random, pronounceable, passphrase-style, PIN, WiFi-ready or business-grade passwords entirely inside your browser. Every password comes with a strength rating, an entropy score, an estimated crack time, and a breakdown of exactly why it's strong or weak \u2014 not just a random string with no context.<\/p>\n<div class=\"formula-box\">\n  <div class=\"f-label\">Entropy formula<\/div>\n  <div class=\"f-eq\">Entropy (bits) = length \u00d7 log\u2082(character pool size)<\/div>\n<\/div>\n<p>Entropy measures how many \"guesses\" an attacker would statistically need. A 16-character password drawn from a pool of 94 possible characters (upper, lower, numbers, symbols) has about 105 bits of entropy \u2014 meaning roughly 2\u00b9\u2070\u2075 possible combinations, which is what drives the crack-time estimate shown after each password you generate.<\/p>\n\n<h2>What makes a password strong<\/h2>\n<table class=\"ref-table\">\n  <thead><tr><th>Factor<\/th><th>Why it matters<\/th><\/tr><\/thead>\n  <tbody>\n    <tr><td>Length<\/td><td>The single biggest factor \u2014 each extra character multiplies the possible combinations<\/td><\/tr>\n    <tr><td>Character variety<\/td><td>Mixing uppercase, lowercase, numbers and symbols expands the pool attackers must search<\/td><\/tr>\n    <tr><td>Randomness<\/td><td>Predictable patterns (keyboard walks, dates, names) are the first things cracking tools try<\/td><\/tr>\n    <tr><td>Uniqueness<\/td><td>Reusing a password means one breached site compromises every account using it<\/td><\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>How hackers actually steal passwords<\/h2>\n<p>Understanding common attack methods explains why the recommendations above matter:<\/p>\n<ul>\n<li><b>Brute-force attacks<\/b> \u2014 automated software tries every possible character combination until one matches. Length and entropy directly determine how long this takes.<\/li>\n<li><b>Dictionary attacks<\/b> \u2014 instead of trying everything, software tries real words, common passwords and known-breach lists first, which is why \"password123\" falls in seconds despite being 11 characters.<\/li>\n<li><b>Credential stuffing<\/b> \u2014 attackers reuse email\/password pairs leaked from one breached site to log into your other accounts, which is why password reuse is so dangerous regardless of how strong the password itself is.<\/li>\n<li><b>Phishing<\/b> \u2014 no password strength helps if you're tricked into typing it into a fake login page, which is why 2FA is a critical second layer of defense.<\/li>\n<\/ul>\n\n<h2>Why a password manager matters<\/h2>\n<p>No one can memorize a unique, high-entropy password for every account. A password manager generates and stores a different strong password per site, so a single breach never cascades into every other account you own \u2014 pairing a tool like this generator with a password manager is the realistic, sustainable way to stay secure.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How secure is this password generator?<\/summary><p>Passwords are generated using your browser's random number generator and are never transmitted, logged or stored anywhere \u2014 generation happens entirely on your device.<\/p><\/details>\n<details><summary>Are generated passwords stored or sent anywhere?<\/summary><p>No. Nothing you generate here leaves your browser. The optional breach-check feature only sends a small partial hash prefix (k-anonymity), never the password itself.<\/p><\/details>\n<details><summary>What is a strong password?<\/summary><p>Generally 12+ characters mixing uppercase, lowercase, numbers and symbols, with no dictionary words or predictable patterns \u2014 though length alone contributes more to strength than complexity does.<\/p><\/details>\n<details><summary>How long should passwords be?<\/summary><p>Most current guidance recommends at least 12\u201316 characters for standard accounts, and longer for highly sensitive accounts like email or financial logins.<\/p><\/details>\n<details><summary>What is password entropy?<\/summary><p>Entropy measures password unpredictability in bits \u2014 higher entropy means exponentially more possible combinations an attacker would need to try.<\/p><\/details>\n<details><summary>Should I reuse passwords across sites?<\/summary><p>No \u2014 use a unique password for every account, ideally stored in a password manager, so a breach on one site can't compromise the others.<\/p><\/details>`,\n\n'unit-conversion':`\n<h2>Unit conversion calculator \u2014 a complete measurement hub<\/h2>\n<p>This calculator covers 16 categories \u2014 length, weight, area, volume, time, speed, pressure, energy, power, force, density, angle, frequency, data storage, temperature, and fuel economy \u2014 with live conversion as you type, every unit shown simultaneously, step-by-step formulas, and a quick-reference table for common values.<\/p>\n\n<h2>How unit conversion works<\/h2>\n<p>Most conversions in this calculator work by converting your value to a common base unit for that category (for example, meters for length, or kilograms for weight), then converting from that base unit into your target unit. This two-step approach means adding new units only requires one conversion factor rather than a separate formula for every possible pair.<\/p>\n<div class=\"formula-box\">\n  <div class=\"f-label\">General formula<\/div>\n  <div class=\"f-eq\">Result = Value \u00d7 (Factor of \"From\" unit \u00f7 Factor of \"To\" unit)<\/div>\n<\/div>\n\n<h2>Why some conversions aren't simple multiplication<\/h2>\n<p><b>Temperature<\/b> doesn't share a common zero point across scales \u2014 0\u00b0C is not 0\u00b0F is not 0K \u2014 so temperature conversions use fixed offset formulas instead of a multiplication factor. <b>Fuel economy<\/b> is a reciprocal relationship: miles-per-gallon measures distance per unit of fuel, while L\/100km measures fuel per unit of distance, so converting between them means inverting the relationship rather than multiplying by a constant.<\/p>\n\n<h2>Metric (SI) vs. imperial\/US customary systems<\/h2>\n<p>The metric system, based on SI (International System of Units), uses consistent powers of 10 (kilo-, centi-, milli-) and is the global scientific and everyday standard in most countries. The imperial\/US customary system (inches, feet, miles, pounds, gallons) remains in everyday use in the United States and, for some measurements, the United Kingdom. Neither system is more \"correct\" \u2014 they're just different standards, which is exactly why conversion tools like this one stay useful.<\/p>\n\n<h2>Binary vs. decimal in data storage<\/h2>\n<p>Storage manufacturers typically use decimal prefixes (1 KB = 1,000 bytes, 1 GB = 1,000,000,000 bytes), following the strict SI definition. Operating systems often report binary values instead (1 KiB = 1,024 bytes, 1 GiB = 1,073,741,824 bytes), because computer memory is naturally organized in powers of two. This is why a drive labeled \"500 GB\" shows as roughly \"465 GB\" in your file manager \u2014 both numbers are correct, they're just using different definitions of \"giga.\"<\/p>\n\n<h2>Precision and rounding<\/h2>\n<p>This calculator displays results to several decimal places for precision, but real-world conversions often only need 1-2 decimal places for practical use. Be cautious with conversions chained across multiple steps (e.g., converting inches to cm to meters to feet) since rounding at each step can compound small errors \u2014 this calculator always converts directly between your selected units in a single step to avoid that.<\/p>\n\n<h2>Common unit conversion mistakes<\/h2>\n<ul>\n<li><b>Confusing mass and weight<\/b> \u2014 kilograms measure mass, not force; pounds are sometimes used for both mass and force depending on context, which occasionally causes confusion in physics contexts.<\/li>\n<li><b>Mixing US and UK gallons\/pints<\/b> \u2014 a US gallon (3.785 L) and a UK\/imperial gallon (4.546 L) differ by about 20%, so recipes, fuel economy figures, and fluid measurements can be significantly off if the wrong one is assumed.<\/li>\n<li><b>Treating MPG conversions as linear<\/b> \u2014 since fuel economy is a reciprocal relationship, \"adding\" MPG values or averaging them directly (rather than through L\/100km) produces mathematically incorrect results.<\/li>\n<li><b>Forgetting temperature offsets<\/b> \u2014 a 10-degree change in Celsius is not the same size as a 10-degree change in Fahrenheit, so temperature differences and absolute temperatures convert differently.<\/li>\n<\/ul>\n\n<h2>Related conversions<\/h2>\n<p>Popular pairs covered here include inches \u2194 centimeters, feet \u2194 meters, miles \u2194 kilometers, kilograms \u2194 pounds, Celsius \u2194 Fahrenheit \u2194 Kelvin, liters \u2194 gallons, PSI \u2194 bar, and megabytes \u2194 gigabytes \u2014 along with less common but still practical categories like pressure, energy, force, and angle conversions for students, engineers, and DIY projects alike.<\/p>\n\n<h2>Frequently asked questions<\/h2>\n<details><summary>How do I convert meters to feet?<\/summary><p>Multiply meters by 3.28084, or simply select Length, choose Meters and Feet, and type your value \u2014 the calculator converts instantly as you type.<\/p><\/details>\n<details><summary>Why are there different measurement systems?<\/summary><p>Historical, political and industrial reasons \u2014 the metric system was designed for scientific consistency and adopted almost globally, while the US retained its customary units from British imperial measures.<\/p><\/details>\n<details><summary>What is an SI unit?<\/summary><p>SI (Syst\u00e8me International) units are the internationally standardized base units of measurement \u2014 meters for length, kilograms for mass, seconds for time, and so on \u2014 that most scientific and metric conversions are built from.<\/p><\/details>\n<details><summary>Why does rounding matter?<\/summary><p>Small rounding errors can compound across multiple conversion steps or large-scale calculations (engineering, construction, cooking at scale), so this calculator always converts directly in one step rather than chaining through intermediate units.<\/p><\/details>\n<details><summary>Are these conversions exact?<\/summary><p>Conversion factors used here (e.g., 1 inch = 2.54 cm exactly) are internationally defined exact values for most units; a few (like average month length) are necessarily approximate and are labeled as such.<\/p><\/details>\n<details><summary>How accurate is the calculator?<\/summary><p>Calculations use double-precision floating point math with standard, internationally recognized conversion factors \u2014 accurate enough for virtually all everyday, academic and professional uses.<\/p><\/details>`\n};\n\n\n\n\/* ===================================================================\n   ROUTING & RENDERING\n=================================================================== *\/\nfunction catBadge(cat){ return `<span class=\"badge-cat\">${CATS[cat].code}<\/span>`; }\n\nfunction renderHomeGrid(filterCat){\n  const grid = $('homeGrid');\n  const items = CALCULATORS.filter(c=>filterCat==='all'||!filterCat||c.cat===filterCat);\n  grid.innerHTML = items.map((c,i)=>`\n    <a class=\"calc-card\" href=\"#\/calculator\/${c.slug}\">\n      <span class=\"code\">${CATS[c.cat].code}-${String(CALCULATORS.filter(x=>x.cat===c.cat).indexOf(c)+1).padStart(2,'0')}<\/span>\n      <div class=\"icn\">${icon(c.icn,24)}<\/div>\n      <h4>${c.name}<\/h4>\n      <p>${c.desc}<\/p>\n      <span class=\"go\">Open calculator \u2192<\/span>\n    <\/a>`).join('');\n}\nfunction initHomeTabs(){\n  const tabs = $('homeTabs');\n  const cats = [['all','All calculators'],...Object.entries(CATS).map(([k,v])=>[k,v.label])];\n  tabs.innerHTML = cats.map(([k,label],i)=>`<button class=\"tab ${i===0?'active':''}\" data-cat=\"${k}\">${label}<\/button>`).join('');\n  tabs.querySelectorAll('.tab').forEach(t=>t.onclick=()=>{\n    tabs.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));\n    t.classList.add('active');\n    renderHomeGrid(t.dataset.cat);\n  });\n  renderHomeGrid('all');\n}\n\nfunction renderListings(filterCat,query){\n  const grid = $('listingsGrid');\n  let items = CALCULATORS.filter(c=>filterCat==='all'||!filterCat||c.cat===filterCat);\n  if(query) items = items.filter(c=>c.name.toLowerCase().includes(query.toLowerCase())||c.desc.toLowerCase().includes(query.toLowerCase()));\n  grid.innerHTML = items.map(c=>`\n    <div class=\"photo-card\">\n      <div class=\"thumb\">\n        <img decoding=\"async\" src=\"https:\/\/picsum.photos\/seed\/maftrix-${c.photo}\/600\/400\" alt=\"${c.name} illustration\" loading=\"lazy\">\n        <span class=\"tag\">${CATS[c.cat].code}<\/span>\n      <\/div>\n      <div class=\"body\">\n        <h4 style=\"margin:0;font-size:1.02rem;\">${c.name}<\/h4>\n        <p>${c.desc}<\/p>\n        <div class=\"foot\">\n          <span style=\"font-size:.78rem;color:var(--text-soft);\">${CATS[c.cat].label}<\/span>\n          <a href=\"#\/calculator\/${c.slug}\" class=\"btn btn-primary btn-sm\">Open \u2192<\/a>\n        <\/div>\n      <\/div>\n    <\/div>`).join('');\n  if(!items.length) grid.innerHTML = `<p style=\"color:var(--text-soft);\">No calculators match your search.<\/p>`;\n}\n\nfunction initListingsPage(){\n  let cat='all', query='';\n  renderListings(cat,query);\n  document.querySelectorAll('#page-listings .chip').forEach(chip=>{\n    chip.onclick=()=>{\n      document.querySelectorAll('#page-listings .chip').forEach(c=>c.classList.remove('active'));\n      chip.classList.add('active');\n      cat=chip.dataset.cat;\n      renderListings(cat,query);\n    };\n  });\n  $('listSearch').oninput = e=>{ query=e.target.value; renderListings(cat,query); };\n}\n\nfunction renderFooterPopular(){\n  const picks = ['mortgage','bmi','percentage','age','tip','gpa'];\n  $('footerPopular').innerHTML = picks.map(slug=>{\n    const c = CALCULATORS.find(x=>x.slug===slug);\n    return c ? `<li><a href=\"#\/calculator\/${c.slug}\">${c.name}<\/a><\/li>` : '';\n  }).join('');\n}\n\nfunction loadCalculator(slug){\n  const c = CALCULATORS.find(x=>x.slug===slug);\n  const mount = $('calcMount');\n  if(!c){\n    $('calcTitle').textContent = 'Calculator not found';\n    $('calcDesc').textContent = 'That calculator doesn\\'t exist yet.';\n    $('calcBreadcrumb').textContent = 'Not found';\n    mount.innerHTML = `<p>Try browsing the <a href=\"#\/listings\">full list of calculators<\/a>.<\/p>`;\n    $('relatedList').innerHTML='';\n    document.title = 'Calculator not found \u2014 MAFtrix';\n    return;\n  }\n  document.title = `${c.name} \u2014 Free Online Calculator | MAFtrix`;\n  const metaDesc = document.querySelector('meta[name=\"description\"]');\n  if(metaDesc) metaDesc.setAttribute('content', c.desc + ' Free, instant, no sign-up \u2014 only on MAFtrix.');\n  $('calcHeadIcon').innerHTML = icon(c.icn,28);\n  $('calcTitle').textContent = c.name;\n  $('calcDesc').textContent = c.desc;\n  $('calcBreadcrumb').textContent = c.name;\n  mount.innerHTML = '';\n  c.render(mount);\n  const related = CALCULATORS.filter(x=>x.cat===c.cat && x.slug!==c.slug).slice(0,6);\n  $('relatedList').innerHTML = related.map(r=>`<a href=\"#\/calculator\/${r.slug}\"><span>${r.name}<\/span> <span>${CATS[r.cat].code}<\/span><\/a>`).join('') || '<li style=\"color:var(--text-soft);font-size:.85rem;\">No related calculators.<\/li>';\n  $('calcArticle').innerHTML = CALC_INFO[c.slug] || '';\n}\n\nconst PAGE_META = {\n  home:{title:'MAFtrix \u2014 Free Online Calculators for Math, Finance & Everyday Life', desc:'MAFtrix offers 31+ free online calculators \u2014 mortgage, loan, BMI, percentage, GPA and more. Fast, accurate, no sign-up.'},\n  about:{title:'About MAFtrix \u2014 Our Story & Principles', desc:'Learn about MAFtrix, a free online calculator hub built for clarity, accuracy and speed.'},\n  services:{title:'Services \u2014 Calculator Categories | MAFtrix', desc:'Explore MAFtrix\\'s four calculator categories: Financial, Math, Health & Fitness, and Everyday tools.'},\n  listings:{title:'All Calculators \u2014 Browse the Full Directory | MAFtrix', desc:'Browse and search all 31+ free calculators on MAFtrix, filterable by category.'},\n  contact:{title:'Contact MAFtrix', desc:'Get in touch with the MAFtrix team \u2014 questions, suggestions, bug reports and advertising inquiries welcome.'}\n};\n\nfunction navigate(){\n  let hash = location.hash.replace('#\/','') || 'home';\n  const parts = hash.split('\/');\n  const route = parts[0] || 'home';\n  document.querySelectorAll('section.page').forEach(s=>s.classList.remove('active'));\n  document.querySelectorAll('nav.links a').forEach(a=>a.classList.remove('active'));\n\n  if(route==='calculator' && parts[1]){\n    $('page-calculator').classList.add('active');\n    loadCalculator(parts[1]);\n  } else {\n    const valid = ['home','about','services','listings','contact'];\n    const r = valid.includes(route) ? route : 'home';\n    $('page-'+r).classList.add('active');\n    const link = document.querySelector(`nav.links a[data-route=\"${r}\"]`);\n    if(link) link.classList.add('active');\n    const meta = PAGE_META[r];\n    if(meta){\n      document.title = meta.title;\n      const metaDesc = document.querySelector('meta[name=\"description\"]');\n      if(metaDesc) metaDesc.setAttribute('content', meta.desc);\n    }\n    if(r==='listings') initListingsPage();\n  }\n  window.scrollTo(0,0);\n  $('siteHeader').classList.remove('open');\n}\n\nwindow.addEventListener('hashchange', navigate);\nwindow.addEventListener('DOMContentLoaded', ()=>{\n  initHomeTabs();\n  renderFooterPopular();\n  $('year').textContent = new Date().getFullYear();\n\n  $('burgerBtn').onclick = ()=>{\n    const open = $('siteHeader').classList.toggle('open');\n    $('burgerBtn').setAttribute('aria-expanded', open);\n  };\n\n  $('heroSearch').addEventListener('submit', e=>{\n    e.preventDefault();\n    const q = $('heroSearchInput').value.trim().toLowerCase();\n    const match = CALCULATORS.find(c=>c.name.toLowerCase().includes(q)||c.slug.includes(q));\n    if(match) location.hash = '#\/calculator\/'+match.slug;\n    else location.hash = '#\/listings';\n  });\n\n  $('contactForm').addEventListener('submit', e=>{\n    e.preventDefault();\n    if(!$('cf-name').value || !$('cf-email').value || !$('cf-message').value) return;\n    $('cf-notice').classList.add('show');\n    e.target.reset();\n  });\n\n  navigate();\n});\n<\/script>\n<\/body>\n<\/html>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Functions | Complete MCQ Quiz (150 Questions) \ud83d\udcd6 Question Navigator 0 \/ 150 Attempted Attempted Unattempted Current Incorrect (Final) Q1 of 150 \u2b50 Score: 0 \/ 150 \u23f1\ufe0f00:00:00 \u25b6\ufe0f Start \u23f9\ufe0f Stop \ud83d\udcd8 \u2190 Prev Next \u2192 \u27f3 Reset<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","_kadence_starter_templates_imported_post":false,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-334","page","type-page","status-publish","hentry"],"taxonomy_info":[],"featured_image_src_large":false,"author_info":{"display_name":"attaurrehman1575@gmail.com","author_link":"https:\/\/mathpk.com\/?author=1"},"comment_info":0,"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"_links":{"self":[{"href":"https:\/\/mathpk.com\/index.php?rest_route=\/wp\/v2\/pages\/334","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mathpk.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mathpk.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mathpk.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mathpk.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=334"}],"version-history":[{"count":3,"href":"https:\/\/mathpk.com\/index.php?rest_route=\/wp\/v2\/pages\/334\/revisions"}],"predecessor-version":[{"id":343,"href":"https:\/\/mathpk.com\/index.php?rest_route=\/wp\/v2\/pages\/334\/revisions\/343"}],"wp:attachment":[{"href":"https:\/\/mathpk.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=334"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}